# Exchange a GitHub Actions ID token for a Plotwork token

> Page: https://docs.plotwork.dev/api/post-oidc-token. Index of every page: https://docs.plotwork.dev/llms.txt

For CI.

`POST /oidc/token`

## TL;DR

| Question | Answer |
|---|---|
| What does it do? | Exchange a GitHub Actions ID token for a Plotwork token. |
| What does it take? | A JSON body of type [`OidcTokenBody`](https://docs.plotwork.dev/api/schemas.md#oidctokenbody). |
| What does it return? | Status 200 with a body of type [`OidcToken`](https://docs.plotwork.dev/api/schemas.md#oidctoken). |
| What can go wrong? | `400` the body is not valid; `401` the ID token failed a check (oidc.token_invalid), or was exchanged already (oidc.token_reused); `403` no trusted publisher trusts this run (oidc.no_trusted_publisher), the organisation's plan has none (plan.trusted_publishers_not_included), or its owner is suspended (auth.account_suspended); `409` several organisations trust this run; name one (oidc.organization_required); `429` too many requests; `503` exchanging is not available on this server, or GitHub's keys cannot be read at the moment (oidc.unavailable). Every failure uses the one error envelope. |

## What it does

For CI. A job of a workflow an organisation trusts sends the ID token GitHub gave it, with this API's origin as the audience. It is checked against GitHub's keys and the organisation's trusted publishers, and the answer is a token that acts as that trusted publisher for 15 minutes on the routes the GitHub Action calls. Only hashes are stored; one ID token is exchanged once. A refusal never says which check failed; the organisation's audit log does.

## Request body

The body has the type [`OidcTokenBody`](https://docs.plotwork.dev/api/schemas.md#oidctokenbody).

| Field | Type | Required | Description |
|---|---|---|---|
| `token` | `string` | yes | The job's GitHub Actions ID token, asked for with this API's origin as the audience. at least 1 character; at most 16384 characters. |
| `organization` | `string` | no | The organisation to sign in to, by slug or id. Needed only when several organisations trust the run. at least 1 character; at most 64 characters. |

## Responses

| Status | Meaning | Body |
|---|---|---|
| 200 | The Plotwork token, when it stops working, and who it acts as. | [`OidcToken`](https://docs.plotwork.dev/api/schemas.md#oidctoken) |
| 400 | The body is not valid. | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 401 | The ID token failed a check (oidc.token_invalid), or was exchanged already (oidc.token_reused). | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 403 | No trusted publisher trusts this run (oidc.no_trusted_publisher), the organisation's plan has none (plan.trusted_publishers_not_included), or its owner is suspended (auth.account_suspended). | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 409 | Several organisations trust this run; name one (oidc.organization_required). | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 429 | Too many requests. | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 503 | Exchanging is not available on this server, or GitHub's keys cannot be read at the moment (oidc.unavailable). | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |

## Response body

A 200 answer has the type [`OidcToken`](https://docs.plotwork.dev/api/schemas.md#oidctoken).

| Field | Type | Required | Description |
|---|---|---|---|
| `token` | `string` | yes | A Plotwork token, `pwo_` and 64 hex characters. Send it as `X-API-Key` or `Authorization: Bearer`. Never stored by Plotwork, only its hash. |
| `tokenType` | `"Bearer"` | yes |  |
| `expiresAt` | `integer` | yes | When it stops working, in milliseconds since the epoch. |
| `expiresIn` | `integer` | yes | Seconds until it stops working. |
| `organization` | `object` | yes |  |
| `organization.id` | `string` | yes |  |
| `organization.slug` | `string` | yes |  |
| `organization.name` | `string` | yes |  |
| `publisher` | `object` | yes |  |
| `publisher.id` | `string` | yes |  |
| `publisher.name` | `string` | yes |  |
| `permissions` | `("diagrams" \| "drift:write")[]` | yes | `diagrams`: publish to the organisation's library (and read it and the style rules). `drift:write`: report drift runs to the Drift tab and read the style rules. |

## See also

| Doc | Why |
|---|---|
| [API reference](https://docs.plotwork.dev/api/reference.md) | Every endpoint, and the error envelope |
| [Schemas](https://docs.plotwork.dev/api/schemas.md) | Every request and response type |
| [Tool reference](https://docs.plotwork.dev/mcp/tools.md) | The operations as callable tools |
