POST /v1/agent-cards/grants
Allow one card to be disclosed to an agent. Signed-in users only — a request authenticated with an API key is refused, because an agent that could grant itself access would make the allowlist meaningless.

Request Body required

Grant details

application/json
One of:
Option 1
Option 2
cardId string REQUIRED
CardID is the Airwallex card this grant allows to be disclosed. At most 255 characters, the width of the column that stores it; a longer value is refused as a 400 rather than reaching Postgres.
connectorId string REQUIRED
ConnectorID names the Airwallex account holding the card. Required: redeeming the grant needs it, and a grant that cannot be redeemed must not be creatable. At most 255 characters, the width of the column that stores it.
maxUses integer
MaxUses is the ceiling on how many handles this grant may ever mint — a grant is not a standing licence to read a card. 1 to 1000; at or below zero is floored to 1.
note string
Note is free text shown alongside the grant. At most 1000 characters.
ttlMinutes integer
TTLMinutes is how long the grant stays valid. No bound is enforced here: a value outside 1 minute to 30 days is clamped to the 30-day maximum.

Responses

200 OK
application/json
code integer
data object
approvedAt string
cardId string
connectorId string
createdAt string
expiresAt string
grantedBy string
id string
maxUses integer
note string
organizationId string
purpose string
requestedBy string
retiredAt string
RetiredAt exists for the unique index, not for the domain. idx_agent_card_grants_live keeps one live grant per card, but a partial index predicate cannot reference now(), so expiry is stamped here instead and the index excludes it. Only ever set on a row that is already past ExpiresAt, and never by a human — a human withdrawing permission sets RevokedAt, which redeem and the UI both read and this column deliberately is not.
revokedAt string
updatedAt string
uses integer
message string
requestId string
400 Bad Request
403 Forbidden
409 Conflict
curl -X POST 'https://api.example.com/v1/agent-cards/grants' \  -H 'Authorization: Bearer YOUR_API_TOKEN' \  -H 'Content-Type: application/json' \  -d '{}'
const response = await fetch('https://api.example.com/v1/agent-cards/grants', {  method: 'POST',  headers: {      "Authorization": "Bearer YOUR_API_TOKEN",      "Content-Type": "application/json"  },  body: JSON.stringify({})});const data = await response.json();console.log(data);
import requestsheaders = {    'Authorization': 'Bearer YOUR_API_TOKEN'}response = requests.post('https://api.example.com/v1/agent-cards/grants', headers=headers, json={})print(response.json())
package mainimport (	"fmt"	"io"	"net/http"	"strings")func main() {	body := strings.NewReader(`{}`)	req, _ := http.NewRequest("POST", "https://api.example.com/v1/agent-cards/grants", body)	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")	req.Header.Set("Content-Type", "application/json")	resp, _ := http.DefaultClient.Do(req)	defer resp.Body.Close()	result, _ := io.ReadAll(resp.Body)	fmt.Println(string(result))}
200 Response
{  "code": 200,  "data": {    "approvedAt": "<string>",    "cardId": "<string>",    "connectorId": "<string>",    "createdAt": "<string>",    "expiresAt": "<string>",    "grantedBy": "<string>",    "id": "<string>",    "maxUses": 123,    "note": "<string>",    "organizationId": "<string>",    "purpose": "<string>",    "requestedBy": "<string>",    "retiredAt": "<string>",    "revokedAt": "<string>",    "updatedAt": "<string>",    "uses": 123  },  "message": "success",  "requestId": "abc-123"}