> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.bibleit.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.bibleit.dev/_mcp/server.

# Edit service-token configuration

PATCH https://api.bibleit.app/v1/organizations/{organization_id}/service-tokens/{credential_id}
Content-Type: application/json

**Required scopes:** `token.create`.

Serializes with other organization credential changes. Preserves omitted fields and expiry. Resulting scopes must not exceed the current issuer scopes, including when scopes are omitted. Expired credentials can be explicitly renewed under the existing active-token cap. Editing does not issue a secret.

**Response headers:**

- `ETag` (200): Configuration validator. Excludes volatile last-use metadata, which is available in the token list only. Rotation invalidates this validator.

Reference: https://docs.bibleit.dev/api-reference/organizations/service-tokens/update-service-token

## Authentication

- `Authorization` header (bearer token, required) — Organization-owned service token. Issue the initial token in organization credential settings. Scopes are documented per operation via x-required-scopes; they are not OAuth scopes.

## Request

### Path parameters

- `organization_id` (string, required) — Immutable organization UUID bound to the service token.
- `credential_id` (string, required)

### Headers

- `If-Match` (string, required) — Use the latest service-token configuration ETag unchanged. Missing: 428; stale: 412. Wildcards, lists and weak validators are rejected.

### Body (application/json)

This endpoint expects a PatchServiceToken.

- `label` (string, optional) — At most 128 UTF-8 bytes.
- `scopes` (list of enum, optional) — Every scope must be held by the issuer; duplicate entries are normalized.
  - Allowed values: `server.get`, `help.get`, `translation.get`, `translation.list`, `translation.read`, `translation.search`, `translation.enable`, `live.get`, `live.list`, `live.subscribe`, `live.create`, `live.update`, `live.delete`, `token.get`, `token.create`, `token.delete`, `live_studio.get`, `live_studio.update`, `presentation.get`, `presentation.list`, `presentation.create`, `presentation.update`, `presentation.delete`, `presentation.export`, `presentation.present`, `organization.get`, `membership.get`, `membership.update`, `membership.delete`, `live.move`, `asset.get`, `asset.create`, `presentation.import`
- `expires_at` (integer, optional, nullable) — Future Unix seconds, at most 90 days ahead. Omit to preserve the exact existing expiry; null explicitly selects no expiration.

## Response

### 200

Successful operation

- `id` (string, required)
- `organization_id` (string, required)
- `label` (string, required)
- `scopes` (list of enum, required)
  - Allowed values: `server.get`, `help.get`, `translation.get`, `translation.list`, `translation.read`, `translation.search`, `translation.enable`, `live.get`, `live.list`, `live.subscribe`, `live.create`, `live.update`, `live.delete`, `token.get`, `token.create`, `token.delete`, `live_studio.get`, `live_studio.update`, `presentation.get`, `presentation.list`, `presentation.create`, `presentation.update`, `presentation.delete`, `presentation.export`, `presentation.present`, `organization.get`, `membership.get`, `membership.update`, `membership.delete`, `live.move`, `asset.get`, `asset.create`, `presentation.import`
- `issued_at` (integer, required)
- `expires_at` (integer, required, nullable) — Unix timestamp in seconds; null means no deadline or no recorded use.

## Errors

### 401 Unauthorized Error

Error response. 400 malformed JSON; 401 invalid/expired/revoked token; 403 denied scope or domain policy; 404 unknown/foreign resource; 405 unsupported method; 409 quota/conflict or stopped Live; 412 stale state validator; 428 missing If-Match; 413 body too large; 415 unsupported media type; 422 invalid fields; 429 admission budget exhausted; 503 unavailable dependency.

- `type` (any, required)
- `title` (string, required)
- `status` (integer, required)
- `code` (string, required)
- `request_id` (string, required)

### 403 Forbidden Error

Error response. 400 malformed JSON; 401 invalid/expired/revoked token; 403 denied scope or domain policy; 404 unknown/foreign resource; 405 unsupported method; 409 quota/conflict or stopped Live; 412 stale state validator; 428 missing If-Match; 413 body too large; 415 unsupported media type; 422 invalid fields; 429 admission budget exhausted; 503 unavailable dependency.

- `type` (any, required)
- `title` (string, required)
- `status` (integer, required)
- `code` (string, required)
- `request_id` (string, required)

### 404 Not Found Error

Error response. 400 malformed JSON; 401 invalid/expired/revoked token; 403 denied scope or domain policy; 404 unknown/foreign resource; 405 unsupported method; 409 quota/conflict or stopped Live; 412 stale state validator; 428 missing If-Match; 413 body too large; 415 unsupported media type; 422 invalid fields; 429 admission budget exhausted; 503 unavailable dependency.

- `type` (any, required)
- `title` (string, required)
- `status` (integer, required)
- `code` (string, required)
- `request_id` (string, required)

### 429 Too Many Requests Error

Error response. 400 malformed JSON; 401 invalid/expired/revoked token; 403 denied scope or domain policy; 404 unknown/foreign resource; 405 unsupported method; 409 quota/conflict or stopped Live; 412 stale state validator; 428 missing If-Match; 413 body too large; 415 unsupported media type; 422 invalid fields; 429 admission budget exhausted; 503 unavailable dependency.

- `type` (any, required)
- `title` (string, required)
- `status` (integer, required)
- `code` (string, required)
- `request_id` (string, required)

### 503 Service Unavailable Error

Error response. 400 malformed JSON; 401 invalid/expired/revoked token; 403 denied scope or domain policy; 404 unknown/foreign resource; 405 unsupported method; 409 quota/conflict or stopped Live; 412 stale state validator; 428 missing If-Match; 413 body too large; 415 unsupported media type; 422 invalid fields; 429 admission budget exhausted; 503 unavailable dependency.

- `type` (any, required)
- `title` (string, required)
- `status` (integer, required)
- `code` (string, required)
- `request_id` (string, required)

## Examples

### Example 1

**Request**

```json
{
  "label": "Integration production"
}
```

**Response**

```json
{
  "id": "string",
  "organization_id": "string",
  "label": "string",
  "scopes": [
    "server.get"
  ],
  "issued_at": 1,
  "expires_at": 1
}
```

**SDK Code**

```python Example 1
import requests

url = "https://api.bibleit.app/v1/organizations/organization_id/service-tokens/credential_id"

payload = { "label": "Integration production" }
headers = {
    "If-Match": "If-Match",
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())
```

```go Example 1
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.bibleit.app/v1/organizations/organization_id/service-tokens/credential_id"

	payload := strings.NewReader("{\n  \"label\": \"Integration production\"\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("If-Match", "If-Match")
	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

### Example 2

**Request**

```json
{
  "scopes": [
    "live.list",
    "live.get"
  ]
}
```

**Response**

```json
{
  "id": "string",
  "organization_id": "string",
  "label": "string",
  "scopes": [
    "server.get"
  ],
  "issued_at": 1,
  "expires_at": 1
}
```

**SDK Code**

```python Example 2
import requests

url = "https://api.bibleit.app/v1/organizations/organization_id/service-tokens/credential_id"

payload = { "scopes": ["live.list", "live.get"] }
headers = {
    "If-Match": "If-Match",
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())
```

```go Example 2
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.bibleit.app/v1/organizations/organization_id/service-tokens/credential_id"

	payload := strings.NewReader("{\n  \"scopes\": [\n    \"live.list\",\n    \"live.get\"\n  ]\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("If-Match", "If-Match")
	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

### Example 3

**Request**

```json
{
  "expires_at": null
}
```

**Response**

```json
{
  "id": "string",
  "organization_id": "string",
  "label": "string",
  "scopes": [
    "server.get"
  ],
  "issued_at": 1,
  "expires_at": 1
}
```

**SDK Code**

```python Example 3
import requests

url = "https://api.bibleit.app/v1/organizations/organization_id/service-tokens/credential_id"

payload = { "expires_at": None }
headers = {
    "If-Match": "If-Match",
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())
```

```go Example 3
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.bibleit.app/v1/organizations/organization_id/service-tokens/credential_id"

	payload := strings.NewReader("{\n  \"expires_at\": null\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("If-Match", "If-Match")
	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```