---
title: "OAuth 2.0 token endpoint"
method: POST
path: "/oauth/token"
tags: ["OAuth"]
---

# OAuth 2.0 token endpoint

`POST /oauth/token`

## Request

- Base URL: `https://api.purplegrain.com`
- URL: `https://api.purplegrain.com/oauth/token`
- Auth: none declared

**Doorkeeper** token endpoint (RFC 6749). Request body must be `application/x-www-form-urlencoded`.

**Supported `grant_type` values:**
- `password` — Resource owner password credentials. Use `username` (user email), `password`, `client_id`, and
  `client_secret` if the OAuth application is confidential. Requires a registered `Doorkeeper::Application`.
- `refresh_token` — `refresh_token`, `client_id`, and `client_secret` (if confidential).
- `client_credentials` — `client_id`, `client_secret`, optional `scope`. No user context: the token has no
  resource owner (RFC 6749 §4.4). For `api`-kind applications with an owner, requests act for that owning
  User/Organization at edit-level authority (destroy-level operations always require a human token).
- `authorization_code` — `code`, `redirect_uri`, `client_id`, `client_secret` (if confidential), and PKCE
  `code_verifier` when the client used `code_challenge` on `/oauth/authorize`.

**First-party apps** without embedding a client secret can use `POST /users/sign-in` or
`POST /users/access-tokens` instead; those return the same token fields plus `user_id` in JSON.

**Revocation:** use `POST /oauth/revoke` or `DELETE /api/v1/users/sign-out` with the access token.

## Response `200`

Token issued.

- DoorkeeperAccessTokenResponse: Access token issued by Doorkeeper. The same fields are returned from `POST /oauth/token` (RFC 6749). First-party endpoints (`POST /users/sign-in`, `POST /users/access-tokens`) add `user_id` for convenience.
  - `access_token` string, required: Opaque bearer token. Send as header Authorization: Bearer {token} on /api/v1/* routes.
  - `token_type` string, required
  - `expires_in` integer: Lifetime of the access token in seconds.
  - `refresh_token` string, nullable: Present when the client and grant type allow refresh tokens.
  - `scope` string: Granted scopes (space-separated).
  - `user_id` string, uuid, nullable: Only returned by first-party JSON endpoints (`/users/sign-in`, `/users/access-tokens`), not by `POST /oauth/token`.
  - `actor_id` string, uuid, nullable: The acting entity's id (first-party endpoints only). Equals `user_id` today; becomes the Organization id when a token is scoped to act for an organization.
  - `actor_type` 'User' | 'Organization', nullable: Polymorphic type of `actor_id` (first-party endpoints only).

## Other responses

- `400`: Invalid request or invalid grant.
- `401`: Invalid client credentials.

---

[API](https://skmtc.dev/purplegrain/apis/purple-grain-api.md) · [All operations](https://skmtc.dev/purplegrain/apis/purple-grain-api/llms.txt) · [OpenAPI document](https://skmtc.dev/purplegrain/apis/purple-grain-api/revisions/4bedbc1c784c?raw)
