Mandates

Create a mandate

Changed on

Creates a mandate denominated in asset. Optionally set policy to cap debits; if omitted, a Coinbase-configured monthly max applies. The response always includes the resolved policy.

Returns the mandate in created status with no source; it is not yet usable. Next step: have the customer approve it with Get wallet approval options then Approve a mandate with a wallet, which attaches the source and moves the mandate to approval_succeeded.

A mandate's terms can't be modified once created. To change them, cancel the mandate and create a new one.

post/v2/mandates

Request

  • Base URL: https://api.cdp.coinbase.com/platform
  • URL: https://api.cdp.coinbase.com/platform/v2/mandates
  • Auth: HTTP bearer

Headers

X-Idempotency-Keystring

An optional string request header for making requests safely retryable. When included, duplicate requests with the same key will return identical responses. Refer to our Idempotency docs for more information on using idempotency keys.

Request body

assetstring required

The symbol of the asset (e.g., eth, usd, usdc, usdt).

expiresAtstring date-time

The UTC ISO 8601 timestamp after which this mandate can no longer be authorized against. Omit for a mandate with no expiry.

metadataMetadata

Optional metadata as key-value pairs. Use this to store additional structured information on a resource, such as customer IDs, order references, or any application-specific data. Up to 10 key/value pairs may be provided. Keys and values are both strings. Keys must be ≤ 40 characters; values must be ≤ 500 characters.

Example request

{
  "asset": "usdc",
  "policy": {
    "maxPerAuthorization": "50.00",
    "maxPerPeriod": {
      "amount": "500.00",
      "period": "month"
    }
  },
  "expiresAt": "2027-06-15T12:00:00.000Z",
  "approvalRedirect": {
    "failureUrl": "https://merchant.example.com/mandate/approval/failed",
    "successUrl": "https://merchant.example.com/mandate/approval/success"
  },
  "revocationRedirect": {
    "failureUrl": "https://merchant.example.com/mandate/revocation/failed",
    "successUrl": "https://merchant.example.com/mandate/revocation/success"
  },
  "customerDisplay": {
    "merchantName": "Acme Store"
  },
  "metadata": {
    "customer_id": "cust_12345"
  }
}

Response

Successfully created mandate.

mandateIdstring required

The ID of the mandate, a UUID prefixed by mandate_.

assetstring required

The symbol of the asset (e.g., eth, usd, usdc, usdt).

status'created' | 'approval_pending' | 'approval_succeeded' | 'approval_failed' | 'revocation_pending' | 'revocation_succeeded' | 'revocation_failed' | 'canceled' required

The most recent action on the mandate. status is an informational indicator of the latest transition; it is not the source of truth for what has durably happened. Read the timestamps for that: approvedAt, canceledAt, and revokedAt each record a durable milestone independently of status.

  • created: the mandate exists but no approval has been attempted. No source attached. Not usable.

  • approval_pending: an approval is in flight. Not usable. Emits acceptance.mandate.approval_initiated.

  • approval_succeeded: the mandate has a source and is usable.

  • approval_failed: the last approval attempt failed. Not usable. Submit a new approval to retry.

  • revocation_pending: a wallet revocation is being confirmed on the network. Emits acceptance.mandate.revocation_initiated. Resolves to revocation_succeeded on success or revocation_failed on failure.

  • revocation_succeeded: a wallet revocation removed the spending allowance on-chain (revokedAt is set). Terminal.

  • revocation_failed: the last wallet revocation attempt did not complete. The spending allowance was not removed, so the mandate remains usable if it was usable before. Submit a new revocation to retry.

  • canceled: the merchant ended the mandate off-chain (canceledAt is set). The on-chain spending allowance may still be live; a later wallet revocation can still move status through the revocation_* values and set revokedAt.

Usable when approvedAt is set, canceledAt is null, revokedAt is null, and expiresAt is null or in the future. Usability is derived from these timestamps, not from status: a failed revocation, for example, leaves status at revocation_failed but the mandate stays usable because the allowance was never removed. Authorization checks the durable conditions in order, and the first match wins: an in-flight approval or revocation returns 409 (mandate_action_pending); revokedAt set returns 422 (mandate_revoked); canceledAt set returns 422 (mandate_canceled); a past expiresAt returns 400 (mandate_expired); any other status returns 422 (mandate_invalid_status). Approve a mandate and get wallet approval options use that same order for the in-flight, revoked, canceled, and expired checks. Any other status except created or approval_failed returns 422 (mandate_invalid_status). Those rejections do not change the timestamps. Debit limits are enforced at authorization time.

expiresAtstring date-time

The UTC ISO 8601 timestamp after which the mandate can no longer be authorized against. Authorization attempts after this time return 422; status does not change. Omit for no expiry.

urlstring uri

A valid HTTP or HTTPS URL.

revocationUrlstring uri

A valid HTTP or HTTPS URL.

approvedAtstring date-time

The UTC ISO 8601 timestamp at which an approval succeeded and the mandate became usable. Present only once the mandate has been approved; set once, on the successful approval, and unchanged by later transitions. Its presence is the durable signal that the mandate was approved, independent of status.

canceledAtstring date-time

The UTC ISO 8601 timestamp at which the merchant canceled the mandate off-chain. Present only once the mandate has been canceled. Canceling does not touch the on-chain spending allowance; check revokedAt for that.

revokedAtstring date-time

The UTC ISO 8601 timestamp at which the spending allowance was removed on-chain by a wallet revocation. Present only once that has happened. Its presence is the single signal that the allowance is gone, independent of status (for example, it can be set on a canceled mandate whose allowance was later cleaned up).

metadataMetadata

Optional metadata as key-value pairs. Use this to store additional structured information on a resource, such as customer IDs, order references, or any application-specific data. Up to 10 key/value pairs may be provided. Keys and values are both strings. Keys must be ≤ 40 characters; values must be ≤ 500 characters.

createdAtstring date-time required

The UTC ISO 8601 timestamp at which the mandate was created.

updatedAtstring date-time required

The UTC ISO 8601 timestamp at which the mandate was last updated.

Example response

{
  "mandateId": "mandate_82c879c1-84e1-44ed-a8c2-1ac239cf09ad",
  "asset": "usdc",
  "source": {
    "address": "0xAbC1234567890aBcDeF1234567890AbCdEf123456",
    "network": "base",
    "asset": "usdc"
  },
  "policy": {
    "maxPerAuthorization": "50.00",
    "maxPerPeriod": {
      "amount": "500.00",
      "period": "month"
    }
  },
  "status": "approval_succeeded",
  "expiresAt": "2027-06-15T12:00:00.000Z",
  "revocationUrl": "https://payments.coinbase.com/mandates/mandate_82c879c1-84e1-44ed-a8c2-1ac239cf09ad/revoke",
  "approvalRedirect": {
    "failureUrl": "https://merchant.example.com/mandate/approval/failed",
    "successUrl": "https://merchant.example.com/mandate/approval/success"
  },
  "revocationRedirect": {
    "failureUrl": "https://merchant.example.com/mandate/revocation/failed",
    "successUrl": "https://merchant.example.com/mandate/revocation/success"
  },
  "customerDisplay": {
    "merchantName": "Acme Store"
  },
  "metadata": {
    "customer_id": "cust_12345"
  },
  "approvedAt": "2025-06-15T12:05:00.000Z",
  "createdAt": "2025-06-15T12:00:00.000Z",
  "updatedAt": "2025-06-15T12:05:00.000Z"
}

Changes