> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grantex.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Tokens

> Exchange, refresh, verify, and revoke grant tokens

## Overview

The `Tokens` service handles the complete token lifecycle: exchanging authorization codes, refreshing tokens, online verification, and revocation.

## Exchange

Exchange an authorization code for a grant token after the user consents.

```go theme={null}
tokenResp, err := client.Tokens.Exchange(ctx, grantex.ExchangeTokenParams{
    Code:    "authorization-code",
    AgentID: "agent-id",
})
```

### Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `Code` | `string` | Yes | Authorization code from callback |
| `AgentID` | `string` | Yes | Agent that requested authorization |
| `CodeVerifier` | `string` | No | PKCE code verifier (required if code challenge was sent) |

### Response (`ExchangeTokenResponse`)

| Field | Type | Description |
| - | - | - |
| `GrantToken` | `string` | JWT grant token |
| `ExpiresAt` | `string` | ISO 8601 grant expiry |
| `Scopes` | `[]string` | Granted scopes |
| `RefreshToken` | `string` | Refresh token for active-grant rotation |
| `GrantID` | `string` | Grant record ID |

## Refresh

Exchange a refresh token for a new grant token while the underlying grant remains active. Refresh tokens are single-use, and each successful rotation returns a new refresh token without extending `ExpiresAt`.

```go theme={null}
tokenResp, err := client.Tokens.Refresh(ctx, grantex.RefreshTokenParams{
    RefreshToken: "current-refresh-token",
    AgentID:      "agent-id",
    // Persist this with the old token until the response is durable.
    IdempotencyKey: refreshAttemptID,
})
// tokenResp.RefreshToken is a NEW refresh token — store it
// tokenResp.ExpiresAt is the same grant expiration
```

<Warning>
  Always store and use the newest `RefreshToken`. Retry a lost response with the same previous token and `IdempotencyKey`. The SDK retains an omitted key for five minutes in the current process, but a restarted caller must restore an explicit persisted key. After the window, once the rotated child token has been used, or after grant expiration, the previous refresh token is rejected.
</Warning>

### Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `RefreshToken` | `string` | Yes | Current refresh token |
| `AgentID` | `string` | Yes | Agent ID |
| `IdempotencyKey` | `string` | No | Stable 16-256 character logical-refresh identifier. Generated and cached in-process when omitted; persist it for restart/failover recovery. |

### Response

Same `ExchangeTokenResponse` as Exchange — includes a new `GrantToken` and `RefreshToken` with the same `GrantID` and `ExpiresAt`.

## Verify

Perform online token verification against the Grantex API.

```go theme={null}
result, err := client.Tokens.Verify(ctx, "grant-token-jwt")
if err != nil {
    log.Fatal(err)
}
if result.Valid {
    fmt.Printf("Grant ID: %s, Scopes: %v\n", *result.GrantID, result.Scopes)
}
```

### Response (`VerifyTokenResponse`)

| Field | Type | Description |
| - | - | - |
| `Valid` | `bool` | Whether the token is valid |
| `GrantID` | `*string` | Grant ID (if valid) |
| `Scopes` | `[]string` | Token scopes (if valid) |
| `Principal` | `*string` | Principal ID (if valid) |
| `Agent` | `*string` | Agent DID (if valid) |
| `ExpiresAt` | `*string` | Token expiry (if valid) |

## Revoke

Revoke a token by its JTI (token ID).

```go theme={null}
err := client.Tokens.Revoke(ctx, "token-jti")
```

Returns `nil` on success (HTTP 204).

## Ownership

Grantex is owned by Orchestrum Technologies LLP. Inventor and owner: Sanjeev Kumar. Ownership contact: [sanjeev@orchestrum.in](mailto:sanjeev@orchestrum.in) or [mishra.sanjeev@gmail.com](mailto:mishra.sanjeev@gmail.com).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.