# Exchange an OIDC token (/docs/api-reference/environment/exchange-oidc-token)

Exchanges an OIDC ID token from a CI or hosting provider (GitHub Actions, Vercel, GitLab, CircleCI or any issuer an environment trusts) for a short-lived access token, so no API key has to be stored there. The token must come from the credential's issuer, be issued for its audience and meet every condition on its claims. Needs no API key. Every refusal is the same `invalid_oidc_token`; the reason is in the environment's logs. `DELETE /v1/api_key` with the access token revokes just that token.

`POST https://api.coss.com/v1/oidc/token`

Authenticate with your API key as a bearer token (`Authorization: Bearer coss_test_…`). Responses are JSON with snake_case fields; errors are `{ error: { type, code, message, param?, issues?, request_id } }` (https://coss.com/docs/api-reference/errors.md).

## Request body

- `credential_id` (string, required, 1-64 characters): The OIDC credential to exchange through (`oidc_…`, shown on the environment's API keys page). Not a secret: it can live in a workflow file.
- `token` (string, required, 1-16384 characters): The OIDC ID token your CI or hosting provider issued to this job or function (a JWT).

## Example request

TypeScript (the `coss` package):

```ts
import { Coss } from "coss";

const coss = new Coss({
  apiKey: process.env.COSS_API_KEY,
  baseUrl: "https://api.coss.com",
});

const { data, error } = await coss.oidc.exchangeToken({
  credential_id: "oidc_01k7c8m4q2f3e9v6t5r1n0p8yz",
  token: "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9.eyJpc3MiOiIuLi4ifQ.c2lnbmF0dXJl",
});
```

HTTP:

```sh
curl -X POST "https://api.coss.com/v1/oidc/token" \
  -H "Authorization: Bearer $COSS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "credential_id": "oidc_01k7c8m4q2f3e9v6t5r1n0p8yz",
  "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9.eyJpc3MiOiIuLi4ifQ.c2lnbmF0dXJl"
}'
```

## Response 201

The access token.

- `object` ("oidc_access_token", required)
- `access_token` (string, required): Use it as an API key (`Authorization: Bearer …`) until it expires. It can do what the credential allows, in the credential's environment.
- `token_type` ("Bearer", required)
- `expires_in` (integer, required): Seconds until it expires.
- `expires_at` (string, required): When it expires (ISO 8601).
- `environment_id` (string, required): The environment it works in.

Example:

```json
{
  "object": "oidc_access_token",
  "access_token": "coss_live_oidc_3q2-7wEVR6aZ1xYb0pN4kL8sT5uC9dF2gH6jM1nP0rQ",
  "token_type": "Bearer",
  "expires_in": 3600,
  "expires_at": "2026-11-13T13:00:00.000Z",
  "environment_id": "env_01k7c8m4q2f3e9v6t5r1n0p8yz"
}
```

## Errors

- `400`: The `Coss-Version` header names an unknown API version (`invalid_api_version`).
- `401`: The ID token wasn't accepted for this credential, or the credential doesn't exist (`invalid_oidc_token`).
- `403`: The credential's organization is suspended (`organization_suspended`).
- `422`: The body is invalid (`invalid_request_error`).
- `429`: Too many exchanges for this credential or from this address, or too many refused tokens from this address. Retry after `Retry-After` seconds.
- `500`: Unexpected server error (`api_error` / `internal_error`).
- `503`: The token couldn't be checked right now; retry (`service_unavailable`).