---
title: "Redeem an approved authorization for an API key"
method: POST
path: "/v2/account/authorize/claim"
tags: ["Authorization"]
---

# Redeem an approved authorization for an API key

`POST /v2/account/authorize/claim`

Exchanges an approved authorization plus the PKCE verifier for an API key. Public: this is where the client gets its first credential.

Browser flow: send the `code` from `/v2/account/authorize/start` and the `redirect_uri` it was started with. Device flow: send the `device_code` and poll — while nobody has approved yet the answer is `AUTHORIZATION_PENDING` and the grant is left untouched, so polling never consumes it.

The key is minted **here**, never earlier, so a secret never sits in Redis waiting to be collected and an authorization nobody redeems leaves no orphan key on the account. It carries the scopes stored at the start of the flow and expires in 90 days; there is no refresh token — when it expires, run the flow again.

A wrong verifier does not burn the authorization: it is put back so the legitimate client can retry, up to a handful of attempts, after which the grant is destroyed. If the redemption succeeds but the response never reaches the client (a dropped connection mid-poll is the normal failure here), retrying with the same code within the next minute returns a working credential for the **same** key instead of minting a second one. If the account is at its key limit, the authorization is left approved so the person can revoke a key and the client can simply retry.

**Clients MUST show `account.email` to the person after connecting.** Approval is what decides which account the key belongs to, and a client cannot tell from the protocol whether the person who approved was its own user — every proof it holds (its code, its verifier) still checks out. Displaying the account is the only thing that lets someone notice they were connected to a stranger's account, which matters most in the device flow, where the code the person reads aloud or types is the whole approval secret.

**The secret is shown once.** No endpoint reads it back.

## Request body

- object — Exactly one of `code` or `device_code`.
  - `code` string — Browser flow grant identifier.
  - `device_code` string — Device flow grant identifier.
  - `verifier` string, required — The PKCE verifier.
  - `redirect_uri` string — Required for the browser flow: must equal the one sent to `/v2/account/authorize/start`.

## Response `200`

Authorization redeemed. `api_key` is shown here and nowhere else, ever.

- object
  - `status` 'success', required
  - `response` object, required
    - `id` string, required
    - `api_key` string, required
    - `account` object, required
      - `id` string, required
      - `email` string, email, required
    - `scopes` string[], required
    - `resources` unknown[], required
      - unknown
    - `expires_at` string, date-time, required

## Other responses

- `400` — `AUTHORIZATION_PENDING` (device flow, keep polling), `INVALID_GRANT`, `INVALID_VERIFIER`, `INVALID_REDIRECT_URI`, `INVALID_REQUEST`, `INVALID_JSON_BODY` or `APIKEY_LIMIT_REACHED`.
- `413` — Body over 2 KB.
- `429` — The endpoint enforces its own rate limit and the caller hit the cooldown.
- `500` — An unexpected error occurred while processing the request.

---

[API](https://skmtc.dev/squarecloud/apis/square-cloud-api.md) · [All operations](https://skmtc.dev/squarecloud/apis/square-cloud-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/squarecloud/square-cloud-api/revisions/54b7733a6b95/schema)
