Credit grants tools
MCP tools for Credit grants: credit_grants_create, credit_grants_list.
credit_grants_create: Grant promo or adjustment creditscredit_grants_list: List a wallet's credit grants
credit_grants_create
Grant credits. Operation credit_grants.create · Brand level · permission wallets:write · risk tier financial · since M6
Grants credits to a wallet without a payment, with a reason:
promo: promotional credits, spent before any other and never refundable;adjustment: a goodwill credit, spent with purchased credits.
In the ledger the credits are a cost (promo_expense); unused promo and adjustment credits that expire reverse
it. expiresAt (optional) is when unused credits expire. A closed wallet takes no grants (wallet_closed); a
frozen one does.
Financial tier: a sign-in in the last 15 minutes for people, and an approval policy may require a second person
above an amount. Dry-run it first. Emits wallet.credited.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (wal_…) | yes | The wallet (wal_…). |
source | one of promo, adjustment | yes | promo: promotional credits (spent first, never refundable). adjustment: a goodwill credit spent with purchased credits. |
amount | object | yes | The credits to grant, in the wallet's currency. |
reason | string | yes | Why the credits are granted; shown on the grant, in the ledger and in the audit log. (1–500 characters) |
expiresAt | date-time (ISO 8601) | no | When unused credits expire (ISO 8601, in the future); they never expire without it. |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
validation_failed: The amount isn't positive, the reason is missing or too long,sourceisn'tpromooradjustment, orexpiresAtisn't in the future.resource_not_found: This brand has no such wallet in this mode (wallets belong to one brand and one mode).wallet_currency_mismatch: The amount isn't in the wallet's currency.wallet_closed: The wallet is closed: reopen it before granting credits.idempotency_key_reused: The idempotency key was used before for another request.version_conflict: The wallet changed at the same moment (another reservation, capture or grant); retry.approval_required: The organization's approval policy needs a second person for this; nothing changed yet.approval_rejected: The same request was sent again after it was rejected.approval_expired: The same request was sent again after its approval request expired.stale_approval: The approved request could no longer run as requested.fresh_auth_required: Your last sign-in is older than 15 minutes.scope_required: The request names no brand (an org or entity key withoutOwnrecord-Brand).permission_denied: You can't change this brand's wallets (wallets:write: owners, admins and support).livemode_not_allowed: You have no live-mode access, so you can't use live wallets.
Example
€5 of welcome credits for a month
Arguments:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"source": "promo",
"amount": {
"amount": "5000000",
"currency": "EUR"
},
"reason": "Welcome credits",
"expiresAt": "2026-11-01T00:00:00Z",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "grant_01m4ty7w2a5qx7mhd9k8w6s0b3",
"object": "credit_grant",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"source": "promo",
"amount": {
"amount": "5000000",
"currency": "EUR"
},
"remaining": {
"amount": "5000000",
"currency": "EUR"
},
"priority": 1,
"expiresAt": "2026-11-01T00:00:00.000Z",
"expired": false,
"reason": "Welcome credits",
"paymentId": null,
"invoiceId": null,
"createdAt": "2026-10-16T10:15:00.000Z"
}
credit_grants_list
List a wallet's credit grants. Operation credit_grants.list · Brand level · permission wallets:read · risk tier read · since M6
Lists the credits behind a wallet's balance, newest first: purchases (top-ups and credit packs), subscription
credits, promo and adjustment grants and refund reversals. remaining is what is left of each grant (credits
held by open reservations count as left); expiresAt is when unused credits expire.
Credits are spent promo first, then subscription credits, then purchased ones, soonest expiry first within each
(spec §17.1, priority).
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (wal_…) | yes | The wallet (wal_…). |
limit | integer | no | (1–100) |
starting_after | ID (grant_…) | no | – |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
validation_failed:idisn't a wallet ID, or a pagination value is invalid.resource_not_found: This brand has no such wallet in this mode (wallets belong to one brand and one mode).scope_required: The request names no brand (an org or entity key withoutOwnrecord-Brand).permission_denied: You can't read this brand's wallets (wallets:read).livemode_not_allowed: You have no live-mode access, so you can't use live wallets.
Example
A promo grant and a purchase
Arguments:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"object": "list",
"data": [
{
"id": "grant_01m4ty7w2a5qx7mhd9k8w6s0b3",
"object": "credit_grant",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"source": "promo",
"amount": {
"amount": "5000000",
"currency": "EUR"
},
"remaining": {
"amount": "5000000",
"currency": "EUR"
},
"priority": 1,
"expiresAt": "2026-11-01T00:00:00.000Z",
"expired": false,
"reason": "Welcome credits",
"paymentId": null,
"invoiceId": null,
"createdAt": "2026-10-16T10:15:00.000Z"
},
{
"id": "grant_01m4ty7w2a5qx7mhd9k8w6s0b2",
"object": "credit_grant",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"source": "purchase",
"amount": {
"amount": "80000000",
"currency": "EUR"
},
"remaining": {
"amount": "80000000",
"currency": "EUR"
},
"priority": 3,
"expiresAt": null,
"expired": false,
"reason": null,
"paymentId": "pay_01m4ty7w2a5qx7mhd9k8w6s0b5",
"invoiceId": null,
"createdAt": "2026-10-14T09:00:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}