Skip to main content

Overview

Consent bundles enable offline authorization for on-device AI agents. These endpoints manage the lifecycle of consent bundles: creation, listing, revocation, and status checks. All endpoints require a developer API key in the Authorization header unless noted otherwise.
Create a new consent bundle for offline operation. The response contains a grant token, JWKS snapshot, Ed25519 audit signing keys, and offline expiry metadata.
POST /v1/consent-bundles

Request Headers

HeaderValue
AuthorizationBearer <api_key>
Content-Typeapplication/json

Request Body

FieldTypeRequiredDescription
agentIdstringYesThe agent to authorize for offline use
userIdstringYesEnd-user / principal ID granting consent
scopesstring[]YesScopes the agent needs while offline
offlineTTLstringNoDuration the bundle is valid offline (default "72h"). Supports h, m, d suffixes. Maximum "168h" (7 days).
offlineAuditKeyAlgorithmstringNoAlgorithm for the audit signing key (default "Ed25519")

Example Request

curl -X POST https://api.grantex.dev/v1/consent-bundles \
  -H "Authorization: Bearer gx_..." \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "ag_01HXYZ...",
    "userId": "user_abc123",
    "scopes": ["calendar:read", "email:send"],
    "offlineTTL": "72h"
  }'

Response — 201 Created

{
  "bundleId": "cb_01HXYZ...",
  "grantToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0xIn0...",
  "jwksSnapshot": {
    "keys": [
      {
        "kty": "RSA",
        "kid": "key-1",
        "n": "0vx7agoebGcQ...",
        "e": "AQAB",
        "alg": "RS256",
        "use": "sig"
      }
    ],
    "fetchedAt": "2026-04-03T12:00:00.000Z",
    "validUntil": "2026-04-10T12:00:00.000Z"
  },
  "offlineAuditKey": {
    "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA...\n-----END PUBLIC KEY-----",
    "privateKey": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIG...\n-----END PRIVATE KEY-----",
    "algorithm": "Ed25519"
  },
  "checkpointAt": 1743681600000,
  "syncEndpoint": "https://api.grantex.dev/v1/audit/offline-sync",
  "offlineExpiresAt": "2026-04-06T12:00:00.000Z"
}

Error Responses

StatusCodeDescription
400INVALID_REQUESTMissing or invalid request fields
401UNAUTHORIZEDInvalid or missing API key
404AGENT_NOT_FOUNDThe specified agent does not exist
422INVALID_SCOPESOne or more scopes are not registered for the agent
422INVALID_TTLofflineTTL exceeds the maximum allowed (168h)
429RATE_LIMITEDToo many requests

SDK Examples

import { createConsentBundle } from '@grantex/gemma';

const bundle = await createConsentBundle({
  apiKey: 'gx_...',
  agentId: 'ag_01HXYZ...',
  userId: 'user_abc123',
  scopes: ['calendar:read', 'email:send'],
  offlineTTL: '72h',
});

List all consent bundles for the authenticated developer.
GET /v1/consent-bundles

Request Headers

HeaderValue
AuthorizationBearer <api_key>

Query Parameters

ParameterTypeRequiredDescription
agentIdstringNoFilter by agent ID
statusstringNoFilter by status: active, expired, revoked
pagenumberNoPage number (default 1)
pageSizenumberNoResults per page (default 20, max 100)

Example Request

curl https://api.grantex.dev/v1/consent-bundles?status=active \
  -H "Authorization: Bearer gx_..."

Response — 200 OK

{
  "bundles": [
    {
      "bundleId": "cb_01HXYZ...",
      "agentId": "ag_01HXYZ...",
      "userId": "user_abc123",
      "scopes": ["calendar:read", "email:send"],
      "offlineExpiresAt": "2026-04-06T12:00:00.000Z",
      "status": "active",
      "createdAt": "2026-04-03T12:00:00.000Z",
      "lastSyncAt": null,
      "auditEntryCount": 0
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 20
}
The list response does not include grantToken or offlineAuditKey.privateKey for security. Only the creation response includes these sensitive fields.

Revoke an active consent bundle. The bundle’s grant token remains cryptographically valid, but the revocation is communicated to the device at the next sync.
POST /v1/consent-bundles/:id/revoke

Path Parameters

ParameterTypeDescription
idstringThe bundle ID to revoke (e.g. cb_01HXYZ...)

Request Headers

HeaderValue
AuthorizationBearer <api_key>

Example Request

curl -X POST https://api.grantex.dev/v1/consent-bundles/cb_01HXYZ.../revoke \
  -H "Authorization: Bearer gx_..."

Response — 200 OK

{
  "bundleId": "cb_01HXYZ...",
  "status": "revoked",
  "revokedAt": "2026-04-03T15:30:00.000Z"
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or missing API key
404BUNDLE_NOT_FOUNDThe specified bundle does not exist
409ALREADY_REVOKEDThe bundle is already revoked

Check Revocation Status

Check whether a consent bundle has been revoked. This endpoint is useful for devices that want to verify bundle validity without performing a full sync.
GET /v1/consent-bundles/:id/revocation-status

Path Parameters

ParameterTypeDescription
idstringThe bundle ID to check

Request Headers

HeaderValue
AuthorizationBearer <api_key>

Example Request

curl https://api.grantex.dev/v1/consent-bundles/cb_01HXYZ.../revocation-status \
  -H "Authorization: Bearer gx_..."

Response — 200 OK

{
  "bundleId": "cb_01HXYZ...",
  "status": "active",
  "revokedAt": null
}
Or if revoked:
{
  "bundleId": "cb_01HXYZ...",
  "status": "revoked",
  "revokedAt": "2026-04-03T15:30:00.000Z"
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or missing API key
404BUNDLE_NOT_FOUNDThe specified bundle does not exist

Refresh an active consent bundle to extend its offline expiry, update the JWKS snapshot, and rotate the audit signing keys.
POST /v1/consent-bundles/:id/refresh

Path Parameters

ParameterTypeDescription
idstringThe bundle ID to refresh

Request Headers

HeaderValue
AuthorizationBearer <api_key>

Example Request

curl -X POST https://api.grantex.dev/v1/consent-bundles/cb_01HXYZ.../refresh \
  -H "Authorization: Bearer gx_..."

Response — 200 OK

Returns a full ConsentBundle object (same schema as the create response) with updated fields.

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or missing API key
404BUNDLE_NOT_FOUNDThe specified bundle does not exist
409BUNDLE_REVOKEDCannot refresh a revoked bundle
409BUNDLE_EXPIREDCannot refresh an expired bundle (create a new one instead)

Rate Limits

EndpointLimit
POST /v1/consent-bundles10 requests/minute
GET /v1/consent-bundles100 requests/minute
POST /v1/consent-bundles/:id/revoke20 requests/minute
GET /v1/consent-bundles/:id/revocation-status100 requests/minute
POST /v1/consent-bundles/:id/refresh10 requests/minute