diff --git a/.claude/skills/grid-api/SKILL.md b/.claude/skills/grid-api/SKILL.md index 53b326149..3c79a2c0f 100644 --- a/.claude/skills/grid-api/SKILL.md +++ b/.claude/skills/grid-api/SKILL.md @@ -151,7 +151,9 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "$GRID_BASE_URL/customers/" | jq . -# Create customer +# Create customer (INDIVIDUAL) +# Optional individual fields: region, currencies, email, phoneNumber, nationality, address, +# birthDate, and idType/identifier (US SSN/ITIN — write-only, never returned). curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -X POST -H "Content-Type: application/json" \ -d '{ @@ -161,6 +163,21 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ }' \ "$GRID_BASE_URL/customers" | jq . +# Create customer (BUSINESS) +# businessInfo REQUIRES legalName, taxId, and incorporatedOn (ISO date YYYY-MM-DD). +curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -X POST -H "Content-Type: application/json" \ + -d '{ + "platformCustomerId": "", + "customerType": "BUSINESS", + "businessInfo": { + "legalName": "Acme Corporation, Inc.", + "taxId": "47-1234567", + "incorporatedOn": "2018-03-14" + } + }' \ + "$GRID_BASE_URL/customers" | jq . + # Update customer (customerType is required) curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -X PATCH -H "Content-Type: application/json" \ @@ -172,9 +189,12 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -X DELETE \ "$GRID_BASE_URL/customers/" | jq . -# Generate KYC link (GET with query params) +# Generate KYC link (POST; Grid customerId in the path, redirectUri in the JSON body) +# Returns kycUrl (hosted flow) plus an optional token (for embedding the provider SDK). curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - "$GRID_BASE_URL/customers/kyc-link?platformCustomerId=&redirectUri=https://example.com/callback" | jq . + -X POST -H "Content-Type: application/json" \ + -d '{"redirectUri": "https://example.com/callback"}' \ + "$GRID_BASE_URL/customers//kyc-link" | jq . ``` ### Account Management @@ -295,7 +315,8 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ }' \ "$GRID_BASE_URL/quotes" | jq . -# Account-funded to external account: IMPORTANT - always include destination currency +# Account-funded to external account: currency is intrinsic to the account, so it is NOT +# sent in the destination. The only optional field on an ACCOUNT destination is paymentRail. curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -X POST -H "Content-Type: application/json" \ -d '{ @@ -305,27 +326,27 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ }, "destination": { "destinationType": "ACCOUNT", - "accountId": "", - "currency": "" + "accountId": "" }, "lockedCurrencyAmount": 10000, "lockedCurrencySide": "SENDING" }' \ "$GRID_BASE_URL/quotes" | jq . -# Real-time/JIT funded: Returns paymentInstructions for funding +# Real-time/JIT funded: Returns paymentInstructions for funding. +# When the funding currency is a stablecoin (USDC/USDT), the source MUST include cryptoNetwork. curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -X POST -H "Content-Type: application/json" \ -d '{ "source": { "sourceType": "REALTIME_FUNDING", "customerId": "", - "currency": "" + "currency": "USDC", + "cryptoNetwork": "SOLANA" }, "destination": { "destinationType": "ACCOUNT", - "accountId": "", - "currency": "" + "accountId": "" }, "lockedCurrencyAmount": 100000, "lockedCurrencySide": "RECEIVING" @@ -346,6 +367,55 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "$GRID_BASE_URL/quotes/" | jq . ``` +**Optional top-level quote fields** (siblings of `source`/`destination`, NOT nested inside +`senderCustomerInfo`): + +- `purposeOfPayment` — enum describing why the payment is being made + (`GOODS_OR_SERVICES`, `GIFT`, `SELF`, `SALARY_PAYMENT`, `FAMILY_SUPPORT`, `OTHER`, and more). + Required for some corridors (e.g. India). +- `remittanceInformation` — free-form memo (max 80 chars) that travels with the payment on the + rail (ACH addenda, FedNow/RTP remittance field, wire OBI). + +**Destination `currency`**: specify it ONLY for `UMA_ADDRESS` destinations. For `ACCOUNT` +destinations the currency is intrinsic to the account and must be omitted; the only optional +field is `paymentRail`. + +### Strong Customer Authentication (SCA) + +Customers in SCA-regulated regions (e.g. the EU) must authorize transfers with a challenge +before funds move. Non-SCA customers never see any of this. Two entry points trigger a +challenge: + +- **Executing a pre-funded quote** — `POST /quotes/{quoteId}/execute` returns the quote in + `PENDING_AUTHORIZATION` with an `scaChallenge` instead of initiating the transfer. Release it + by authorizing the quote you already hold; **re-calling execute returns 409**. +- **Creating a realtime-funding quote** — `POST /quotes` may return `202` with the quote in + `PENDING_AUTHORIZATION`, withholding `paymentInstructions` until the challenge is authorized. + +Authorize the challenge (sandbox SMS code is always `123456`): + +```bash +curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -X POST -H "Content-Type: application/json" \ + -d '{"code": "123456"}' \ + "$GRID_BASE_URL/quotes//authorize" | jq . +``` + +The updated quote is returned. If it is still `PENDING_AUTHORIZATION` it carries the next +`scaChallenge` — authorize again until it advances (its `paymentInstructions` populate for +realtime-funding quotes). + +Resend an expired SMS code (reuses the same challenge; `PASSKEY` challenges cannot be re-sent): + +```bash +curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -X POST \ + "$GRID_BASE_URL/quotes//authorize/resend" +``` + +Optionally request a preferred challenge factor with `scaFactor` on quote-create or execute — +values `SMS_OTP` (default) or `PASSKEY`. + ### Same-Currency Transfers ```bash @@ -360,12 +430,15 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "$GRID_BASE_URL/transfer-in" | jq . # Transfer out (internal → external, same currency) +# Optional: top-level "remittanceInformation" (memo, max 80 chars) and a "paymentRail" +# inside the destination to pick a specific supported rail. curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -X POST -H "Content-Type: application/json" \ -d '{ "source": {"accountId": ""}, - "destination": {"accountId": ""}, - "amount": 10000 + "destination": {"accountId": "", "paymentRail": ""}, + "amount": 10000, + "remittanceInformation": "Invoice 1234" }' \ "$GRID_BASE_URL/transfer-out" | jq . ``` @@ -502,7 +575,7 @@ Use this flow when the user asks for a "realtime quote" or "just in time" funded 4. **Choose the right flow**: Use prefunded for immediate execution, JIT for crypto/instant rails 5. **Pipe through jq**: Always append `| jq .` for readable output, or `| jq -r .field` to extract specific values 6. **URL-encode special characters**: UMA addresses contain `$` — encode as `%24` in URL paths -7. **Always include destination currency in quotes**: When specifying a destination account, you MUST include `currency` in the destination object even though the external account already has a currency +7. **Destination currency is only for UMA**: Specify `currency` in the destination object ONLY for `UMA_ADDRESS` destinations. For `ACCOUNT` destinations the currency is intrinsic to the account and must be omitted — the only optional field there is `paymentRail` 8. **Individual beneficiary requires fullName**: For `beneficiaryType: "INDIVIDUAL"`, `fullName` is required. `birthDate` (YYYY-MM-DD) and `nationality` (2-letter code) are optional but recommended 9. **Use correct Nigerian field names**: Use `bankName` (NOT `bankCode`) and include `purposeOfPayment` 10. **Don't forget country-specific required fields**: Brazil (BRL_ACCOUNT) requires `pixKey`, `pixKeyType`, and `taxId`; Europe (EUR_ACCOUNT) requires `iban`; all fiat accounts require `paymentRails` diff --git a/.claude/skills/grid-api/references/account-types.md b/.claude/skills/grid-api/references/account-types.md index fcc1748c8..a9e2fc3a3 100644 --- a/.claude/skills/grid-api/references/account-types.md +++ b/.claude/skills/grid-api/references/account-types.md @@ -4,25 +4,38 @@ This reference maps countries/regions to their required account types and fields ## Account Type Summary -| Country/Region | Account Type | Currency | Payment Rail(s) | Primary Identifier | +The **Available Rails** column lists the payment rails Grid may use for the created account. The rail is chosen by Grid and returned on the created account (the response `paymentRails` field) — it is not a request input. + +| Country/Region | Account Type | Currency | Available Rails | Primary Identifier | |----------------|--------------|----------|-----------------|-------------------| +| Bangladesh | BDT_ACCOUNT | BDT | BANK_TRANSFER, MOBILE_MONEY | Bank name + Account number | | Botswana | BWP_ACCOUNT | BWP | MOBILE_MONEY | Phone number + Provider | | Brazil | BRL_ACCOUNT | BRL | PIX | PIX key | | Canada | CAD_ACCOUNT | CAD | BANK_TRANSFER | Bank code + Branch code + Account number | | Central Africa (CFA) | XAF_ACCOUNT | XAF | MOBILE_MONEY | Phone number + Provider + Region | +| China | CNY_ACCOUNT | CNY | BANK_TRANSFER, MOBILE_MONEY | Bank name + Account number | +| Colombia | COP_ACCOUNT | COP | BANK_TRANSFER, MOBILE_MONEY | Bank name + Account number + Account type | | Denmark | DKK_ACCOUNT | DKK | SEPA, SEPA_INSTANT | IBAN | +| Egypt | EGP_ACCOUNT | EGP | BANK_TRANSFER, MOBILE_MONEY | Bank name + IBAN | +| El Salvador | SLV_ACCOUNT | SLV | BANK_TRANSFER, MOBILE_MONEY | Account number or Phone number | | Europe (SEPA) | EUR_ACCOUNT | EUR | SEPA, SEPA_INSTANT | IBAN | +| Ghana | GHS_ACCOUNT | GHS | BANK_TRANSFER, MOBILE_MONEY | Bank name + Account number | +| Guatemala | GTQ_ACCOUNT | GTQ | BANK_TRANSFER | Bank name + Account number + Account type | +| Haiti | HTG_ACCOUNT | HTG | MOBILE_MONEY | Phone number | | Hong Kong | HKD_ACCOUNT | HKD | BANK_TRANSFER | Bank name + SWIFT + Account number | -| India | INR_ACCOUNT | INR | UPI | UPI VPA (user@bank) | +| India | INR_ACCOUNT | INR | UPI, NEFT, RTGS | UPI VPA, or Account number + IFSC | | Indonesia | IDR_ACCOUNT | IDR | BANK_TRANSFER | Bank name + SWIFT + Account number + Phone | +| International (SWIFT) | SWIFT_ACCOUNT | Multiple | SWIFT | SWIFT code + Bank name + Account number/IBAN | +| Jamaica | JMD_ACCOUNT | JMD | BANK_TRANSFER | Bank name + Account number + Branch code | | Kenya | KES_ACCOUNT | KES | MOBILE_MONEY | Phone number + Provider | | Malawi | MWK_ACCOUNT | MWK | MOBILE_MONEY | Phone number + Provider | | Malaysia | MYR_ACCOUNT | MYR | BANK_TRANSFER | Bank name + SWIFT + Account number | | Mexico | MXN_ACCOUNT | MXN | SPEI | 18-digit CLABE number | | Nigeria | NGN_ACCOUNT | NGN | BANK_TRANSFER | Bank name + Account number | +| Pakistan | PKR_ACCOUNT | PKR | BANK_TRANSFER, MOBILE_MONEY | Bank name + Account number/IBAN | | Philippines | PHP_ACCOUNT | PHP | BANK_TRANSFER | Bank name + Account number | | Rwanda | RWF_ACCOUNT | RWF | MOBILE_MONEY | Phone number + Provider | -| Singapore | SGD_ACCOUNT | SGD | PAYNOW, FAST, BANK_TRANSFER | Bank name + SWIFT + Account number | +| Singapore | SGD_ACCOUNT | SGD | PAYNOW, FAST, BANK_TRANSFER | SWIFT + Account number | | South Africa | ZAR_ACCOUNT | ZAR | BANK_TRANSFER | Bank name + Account number | | Tanzania | TZS_ACCOUNT | TZS | MOBILE_MONEY | Phone number + Provider | | Thailand | THB_ACCOUNT | THB | BANK_TRANSFER | Bank name + SWIFT + Account number | @@ -31,7 +44,7 @@ This reference maps countries/regions to their required account types and fields | United Kingdom | GBP_ACCOUNT | GBP | FASTER_PAYMENTS | Sort code + Account number | | United States | USD_ACCOUNT | USD | ACH, WIRE, RTP, FEDNOW | Routing number + Account number | | Vietnam | VND_ACCOUNT | VND | BANK_TRANSFER | Bank name + SWIFT + Account number | -| West Africa (CFA) | XOF_ACCOUNT | XOF | MOBILE_MONEY | Phone number + Provider | +| West Africa (CFA) | XOF_ACCOUNT | XOF | MOBILE_MONEY | Phone number + Provider + Region | | Zambia | ZMW_ACCOUNT | ZMW | MOBILE_MONEY | Phone number + Provider | ## Crypto/Wallet Types @@ -48,7 +61,12 @@ This reference maps countries/regions to their required account types and fields ## Detailed Field Requirements -**IMPORTANT**: All fiat account types now require a `paymentRails` field specifying the payment rail to use. +**Note on payment rails:** You do not supply a payment rail when creating a fiat account. Grid selects the appropriate rail (or set of rails) for the account and returns it on the created account's `paymentRails` field in the response. The **Available Rails** column above is informational. + +**Optional top-level create fields** (alongside `customerId`, `currency`, and `accountInfo` on the create request): + +- `platformAccountId`: Your platform's own identifier for the account in your system (e.g. `ext_acc_123456`). Echoed back so you can reference the account by your identifier. +- `defaultUmaDepositAccount`: Boolean, defaults to `false`. When `true`, incoming payments to this customer's UMA address are automatically deposited into this account. Only one account per customer can be the default; setting a new one overrides any existing default. ### Botswana (BWP_ACCOUNT) @@ -60,7 +78,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "BWP", "accountInfo": { "accountType": "BWP_ACCOUNT", - "paymentRails": ["MOBILE_MONEY"], "phoneNumber": "+2671234567", "provider": "Orange Money", "beneficiary": { @@ -76,7 +93,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `MOBILE_MONEY` - `phoneNumber`: International format (pattern: `^\+[0-9]{6,14}$`) - `provider`: Mobile money provider name @@ -90,7 +106,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "XAF", "accountInfo": { "accountType": "XAF_ACCOUNT", - "paymentRails": ["MOBILE_MONEY"], "phoneNumber": "+237612345678", "provider": "MTN Mobile Money", "region": "CM", @@ -107,7 +122,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `MOBILE_MONEY` - `phoneNumber`: International format (pattern: `^\+[0-9]{6,14}$`) - `provider`: Mobile money provider name - `region`: Country code in Central African CFA franc zone (enum: `CM` for Cameroon, `CG` for Congo) @@ -124,7 +138,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "MXN", "accountInfo": { "accountType": "MXN_ACCOUNT", - "paymentRails": ["SPEI"], "clabeNumber": "<18-digit-clabe>", "beneficiary": { "beneficiaryType": "INDIVIDUAL", @@ -147,7 +160,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "MXN", "accountInfo": { "accountType": "MXN_ACCOUNT", - "paymentRails": ["SPEI"], "clabeNumber": "<18-digit-clabe>", "beneficiary": { "beneficiaryType": "BUSINESS", @@ -174,7 +186,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "BRL", "accountInfo": { "accountType": "BRL_ACCOUNT", - "paymentRails": ["PIX"], "pixKey": "12345678901", "pixKeyType": "CPF", "taxId": "12345678901", @@ -189,7 +200,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `PIX` - `pixKey`: The PIX key value - `pixKeyType`: One of `CPF`, `CNPJ`, `EMAIL`, `PHONE`, `RANDOM` - `taxId`: Tax ID of the account holder @@ -212,14 +222,14 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "EUR", "accountInfo": { "accountType": "EUR_ACCOUNT", - "paymentRails": ["SEPA_INSTANT"], "iban": "DE89370400440532013000", - "swiftBic": "COBADEFFXXX", + "swiftCode": "COBADEFFXXX", "beneficiary": { "beneficiaryType": "INDIVIDUAL", "fullName": "Full Name", "birthDate": "1990-01-15", "nationality": "DE", + "countryOfResidence": "DE", "address": { "line1": "123 Street", "city": "Berlin", @@ -234,9 +244,9 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `SEPA` or `SEPA_INSTANT` - `iban`: International Bank Account Number (15-34 characters) -- `swiftBic`: SWIFT/BIC code (8 or 11 characters, pattern: `^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$`) — optional +- `swiftCode`: SWIFT/BIC code (8 or 11 characters, pattern: `^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$`) — optional +- Beneficiary: `countryOfResidence` is required (in addition to `fullName`) ### Denmark (DKK_ACCOUNT) @@ -250,7 +260,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "DKK", "accountInfo": { "accountType": "DKK_ACCOUNT", - "paymentRails": ["SEPA"], "iban": "DK5000400440116243", "beneficiary": { "beneficiaryType": "INDIVIDUAL", @@ -265,9 +274,8 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `SEPA` or `SEPA_INSTANT` - `iban`: IBAN number -- `swiftBic`: optional +- `swiftCode`: optional ### United States (USD_ACCOUNT) @@ -279,7 +287,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "USD", "accountInfo": { "accountType": "USD_ACCOUNT", - "paymentRails": ["ACH"], "routingNumber": "123456789", "accountNumber": "12345678901", "beneficiary": { @@ -302,12 +309,15 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `ACH`, `WIRE`, `RTP`, or `FEDNOW` - `routingNumber`: 9-digit ABA routing number - `accountNumber`: Bank account number ### India (INR_ACCOUNT) +INR supports two payout paths: UPI, or a NEFT/RTGS bank transfer. + +**UPI:** + ```bash curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ -X POST -H "Content-Type: application/json" \ @@ -316,7 +326,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "INR", "accountInfo": { "accountType": "INR_ACCOUNT", - "paymentRails": ["UPI"], "vpa": "user@okbank", "beneficiary": { "beneficiaryType": "INDIVIDUAL", @@ -331,6 +340,38 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ UPI VPA format: `username@bankhandle` (e.g., `john@okaxis`, `jane@ybl`) +**NEFT/RTGS bank transfer:** + +```bash +curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -X POST -H "Content-Type: application/json" \ + -d '{ + "customerId": "", + "currency": "INR", + "accountInfo": { + "accountType": "INR_ACCOUNT", + "accountNumber": "000111222333", + "ifsc": "HDFC0001234", + "rail": "NEFT", + "bankName": "HDFC Bank", + "beneficiary": { + "beneficiaryType": "INDIVIDUAL", + "fullName": "Full Name", + "birthDate": "1990-01-15", + "nationality": "IN" + } + } + }' \ + "$GRID_BASE_URL/customers/external-accounts" | jq . +``` + +Bank-transfer fields: + +- `accountNumber`: Indian bank account number, 9–18 digits (pattern: `^[0-9]{9,18}$`) +- `ifsc`: IFSC of the beneficiary's bank branch (pattern: `^[A-Z]{4}0[A-Z0-9]{6}$`) +- `rail`: `NEFT` or `RTGS` +- `bankName`: Name of the bank (optional) + ### UAE (AED_ACCOUNT) ```bash @@ -341,14 +382,19 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "AED", "accountInfo": { "accountType": "AED_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "iban": "AE070331234567890123456", "swiftCode": "EBILAEAD", "beneficiary": { "beneficiaryType": "INDIVIDUAL", "fullName": "Full Name", "birthDate": "1990-01-15", - "nationality": "AE" + "nationality": "AE", + "address": { + "line1": "123 Street", + "city": "Dubai", + "postalCode": "00000", + "country": "AE" + } } } }' \ @@ -357,9 +403,9 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `iban`: UAE IBAN (23 characters, pattern: `^AE[0-9]{21}$`) - `swiftCode`: SWIFT/BIC code (8 or 11 characters) — optional +- Beneficiary: `address` is required (in addition to `fullName`) ### United Kingdom (GBP_ACCOUNT) @@ -371,8 +417,7 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "GBP", "accountInfo": { "accountType": "GBP_ACCOUNT", - "paymentRails": ["FASTER_PAYMENTS"], - "sortCode": "12-34-56", + "sortCode": "123456", "accountNumber": "12345678", "beneficiary": { "beneficiaryType": "INDIVIDUAL", @@ -387,8 +432,7 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `FASTER_PAYMENTS` -- `sortCode`: 6-digit sort code (pattern: `^[0-9]{2}-?[0-9]{2}-?[0-9]{2}$`) +- `sortCode`: 6-digit sort code, no separators (pattern: `^[0-9]{6}$`, e.g. `123456`) - `accountNumber`: 8-digit account number (pattern: `^[0-9]{8}$`) ### Nigeria (NGN_ACCOUNT) @@ -401,7 +445,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "NGN", "accountInfo": { "accountType": "NGN_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "accountNumber": "1234567890", "bankName": "GTBank", "beneficiary": { @@ -417,7 +460,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `accountNumber`: 10-digit account number (pattern: `^[0-9]{10}$`) - `bankName`: Bank name (e.g., "GTBank", "Zenith Bank", "Access Bank") - For individuals: `fullName`, `birthDate`, `nationality` @@ -435,7 +477,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "CAD", "accountInfo": { "accountType": "CAD_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankCode": "001", "branchCode": "00012", "accountNumber": "1234567", @@ -452,7 +493,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankCode`: 3-digit financial institution number (pattern: `^[0-9]{3}$`) - `branchCode`: 5-digit transit number (pattern: `^[0-9]{5}$`) - `accountNumber`: 7-12 digit account number (pattern: `^[0-9]{7,12}$`) @@ -467,7 +507,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "PHP", "accountInfo": { "accountType": "PHP_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankName": "BDO Unibank", "accountNumber": "1234567890", "beneficiary": { @@ -483,7 +522,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankName`: Name of the bank - `accountNumber`: Bank account number @@ -497,7 +535,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "SGD", "accountInfo": { "accountType": "SGD_ACCOUNT", - "paymentRails": ["FAST"], "bankName": "DBS Bank", "swiftCode": "DBSSSGSG", "accountNumber": "1234567890", @@ -514,11 +551,13 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `PAYNOW`, `FAST`, or `BANK_TRANSFER` -- `bankName`: Name of the bank - `swiftCode`: SWIFT/BIC code (8 or 11 characters) - `accountNumber`: Bank account number +Optional fields: + +- `bankName`: Name of the bank. When omitted, it is resolved from `swiftCode` via the payout partner bank directory at account creation. + ### Hong Kong (HKD_ACCOUNT) ```bash @@ -529,7 +568,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "HKD", "accountInfo": { "accountType": "HKD_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankName": "HSBC", "swiftCode": "HSBCHKHH", "accountNumber": "123456789012", @@ -546,7 +584,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankName`: Name of the bank - `swiftCode`: SWIFT/BIC code (8 or 11 characters) - `accountNumber`: Bank account number @@ -561,7 +598,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "IDR", "accountInfo": { "accountType": "IDR_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankName": "Bank Central Asia", "swiftCode": "CENAIDJA", "accountNumber": "1234567890", @@ -579,7 +615,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankName`: Name of the bank - `swiftCode`: SWIFT/BIC code - `accountNumber`: Bank account number @@ -595,7 +630,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "MYR", "accountInfo": { "accountType": "MYR_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankName": "Maybank", "swiftCode": "MABORJMJXXX", "accountNumber": "1234567890", @@ -612,7 +646,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankName`, `swiftCode`, `accountNumber` ### Thailand (THB_ACCOUNT) @@ -625,7 +658,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "THB", "accountInfo": { "accountType": "THB_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankName": "Bangkok Bank", "swiftCode": "BKKBTHBK", "accountNumber": "1234567890", @@ -642,7 +674,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankName`, `swiftCode`, `accountNumber` ### Vietnam (VND_ACCOUNT) @@ -655,7 +686,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "VND", "accountInfo": { "accountType": "VND_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankName": "Vietcombank", "swiftCode": "BFTVVNVX", "accountNumber": "1234567890", @@ -672,7 +702,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankName`, `swiftCode`, `accountNumber` ### South Africa (ZAR_ACCOUNT) @@ -685,7 +714,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "ZAR", "accountInfo": { "accountType": "ZAR_ACCOUNT", - "paymentRails": ["BANK_TRANSFER"], "bankName": "Standard Bank", "accountNumber": "1234567890", "beneficiary": { @@ -701,7 +729,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Required fields: -- `paymentRails`: `BANK_TRANSFER` - `bankName`: Name of the bank - `accountNumber`: 9-13 digit account number (pattern: `^[0-9]{9,13}$`) @@ -719,7 +746,6 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "KES", "accountInfo": { "accountType": "KES_ACCOUNT", - "paymentRails": ["MOBILE_MONEY"], "phoneNumber": "+254712345678", "provider": "M-Pesa", "beneficiary": { @@ -743,11 +769,11 @@ Phone pattern: `^\+255[0-9]{9}$` #### Uganda (UGX_ACCOUNT) -Phone pattern: `^\+256[0-9]{9}$` +Phone pattern: `^\+[0-9]{6,14}$` #### Malawi (MWK_ACCOUNT) -Phone pattern: `^\+265[0-9]{9}$` +Phone pattern: `^\+[0-9]{6,14}$` #### Zambia (ZMW_ACCOUNT) @@ -755,7 +781,7 @@ Phone pattern: `^\+260[0-9]{9}$` #### West Africa CFA (XOF_ACCOUNT) -Supports Senegal (+221), Ivory Coast (+225), and Benin (+229): +Supports Benin (+229), Ivory Coast (+225), Senegal (+221), and Togo: ```bash curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ @@ -765,8 +791,7 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "currency": "XOF", "accountInfo": { "accountType": "XOF_ACCOUNT", - "paymentRails": ["MOBILE_MONEY"], - "countries": ["SN"], + "region": "SN", "phoneNumber": "+221771234567", "provider": "Orange Money", "beneficiary": { @@ -782,10 +807,103 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ Mobile money required fields: -- `paymentRails`: `MOBILE_MONEY` - `phoneNumber`: Country-specific format (see patterns above) - `provider`: Mobile money provider name -- `countries`: Required for XOF_ACCOUNT (enum: `SN`, `BJ`, `CI`), XAF_ACCOUNT uses `region` (enum: `CM`, `CG`), MWK_ACCOUNT (`MW`), UGX_ACCOUNT (`UG`) +- `region`: Required for XOF_ACCOUNT (single country code, enum: `BJ`, `CI`, `SN`, `TG`). XAF_ACCOUNT also requires `region` (enum: `CM`, `CG`). Other mobile-money account types (e.g. MWK_ACCOUNT, UGX_ACCOUNT) require only `phoneNumber` + `provider`. + +### Additional Fiat Account Types + +Each type below shares the common request envelope (`customerId`, `currency`, `accountInfo`) and beneficiary structure. Only the type-specific `accountInfo` fields are listed. + +#### Bangladesh (BDT_ACCOUNT) + +- `bankName`: Name of the bank (required) +- Bank transfer: `accountNumber`; optional `branchCode` (5 digits, pattern `^[0-9]{5}$`) and `swiftCode` +- Mobile money: `phoneNumber` (pattern: `^\+[0-9]{6,14}$`) + +#### China (CNY_ACCOUNT) + +- `bankName`: Name of the bank (required) +- Bank transfer: `accountNumber` +- Mobile money: `phoneNumber` (pattern: `^\+[0-9]{6,14}$`) + +#### Colombia (COP_ACCOUNT) + +- `bankName`: Name of the bank (required) +- Bank transfer: `accountNumber` + `bankAccountType` (`CHECKING` or `SAVINGS`) +- Mobile money: `phoneNumber` (pattern: `^\+[0-9]{6,14}$`) +- Beneficiary may include Colombia-specific `documentType` (`CC`, `CE`, `TI`, `NIT`, `PP`) and `documentNumber` (both optional) + +#### Egypt (EGP_ACCOUNT) + +- `bankName`: Name of the bank (required) +- Bank transfer: `iban` (Egyptian IBAN, 29 characters, pattern `^EG[0-9]{27}$`) +- Mobile money: `phoneNumber` (pattern: `^\+[0-9]{6,14}$`) + +#### El Salvador (SLV_ACCOUNT) + +- Bank transfer: `accountNumber` + `bankAccountType` (`CHECKING` or `SAVINGS`); optional `bankName` +- Mobile money: `phoneNumber` (pattern: `^\+[0-9]{6,14}$`, e.g. Tigo Money) + +#### Ghana (GHS_ACCOUNT) + +- `bankName`: Name of the bank (required) +- Bank transfer: `accountNumber` +- Mobile money: `phoneNumber` (pattern: `^\+[0-9]{6,14}$`) + +#### Guatemala (GTQ_ACCOUNT) + +- `accountNumber`, `bankAccountType` (`CHECKING` or `SAVINGS`), and `bankName` (all required) +- Beneficiary requires `countryOfResidence` and `phoneNumber` (in addition to `fullName`) + +#### Haiti (HTG_ACCOUNT) + +- `phoneNumber`: International format (pattern: `^\+[0-9]{6,14}$`) (required) + +#### Jamaica (JMD_ACCOUNT) + +- `accountNumber`, `branchCode` (5 digits, pattern `^[0-9]{5}$`), `bankAccountType` (`CHECKING` or `SAVINGS`), and `bankName` (all required) +- Beneficiary requires `address` and `phoneNumber` (in addition to `fullName`) + +#### Pakistan (PKR_ACCOUNT) + +- `bankName`: Name of the bank (required) +- Bank transfer: `accountNumber`, or `iban` (Pakistani IBAN, 24 characters, pattern `^PK[0-9]{2}[A-Z]{4}[0-9]{16}$`) +- Mobile money: `phoneNumber` (pattern: `^\+[0-9]{6,14}$`) + +#### International (SWIFT_ACCOUNT) + +Cross-border SWIFT transfer for corridors without a dedicated account type. + +```bash +curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -X POST -H "Content-Type: application/json" \ + -d '{ + "customerId": "", + "currency": "USD", + "accountInfo": { + "accountType": "SWIFT_ACCOUNT", + "country": "NG", + "swiftCode": "DEUTDEFF", + "bankName": "Deutsche Bank", + "accountNumber": "1234567890", + "beneficiary": { + "beneficiaryType": "INDIVIDUAL", + "fullName": "Full Name", + "birthDate": "1990-01-15", + "nationality": "NG" + } + } + }' \ + "$GRID_BASE_URL/customers/external-accounts" | jq . +``` + +Required fields: + +- `country`: ISO 3166-1 alpha-2 country code of the bank account (pattern: `^[A-Z]{2}$`) +- `swiftCode`: SWIFT/BIC code (8 or 11 characters) +- `bankName`: Name of the bank +- Either `accountNumber` OR `iban` — use `iban` for IBAN-only corridors (e.g. GB, BR), `accountNumber` otherwise ### Crypto Wallets @@ -875,11 +993,21 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - `fullName`: Full legal name (REQUIRED) - `birthDate`: Date of birth (YYYY-MM-DD format) - `nationality`: 2-letter ISO country code (e.g., NG, MX, IN, US) +- `email`: Email address of the beneficiary +- `phoneNumber`: Phone number of the beneficiary +- `countryOfResidence`: 2-letter ISO country code of residence - `address`: Object with `line1`, `city`, `state`, `postalCode`, `country` +Some corridors promote these optional fields to required — for example EUR and GTQ require `countryOfResidence`, AED and JMD require `address`, and GTQ and JMD require `phoneNumber`. See each account type above. + ### For Businesses (beneficiaryType: "BUSINESS") - `legalName`: Registered business/legal name (REQUIRED) +- `registrationNumber`: Business registration number (optional) +- `taxId`: Business tax identification number (optional) +- `email`: Email address of the beneficiary (optional) +- `phoneNumber`: Phone number of the beneficiary (optional) +- `countryOfResidence`: 2-letter ISO country code of residence (optional) - `address`: Business address (optional) ## Sandbox Testing diff --git a/.claude/skills/grid-api/references/endpoints.md b/.claude/skills/grid-api/references/endpoints.md index da3ca698c..e7550a096 100644 --- a/.claude/skills/grid-api/references/endpoints.md +++ b/.claude/skills/grid-api/references/endpoints.md @@ -8,7 +8,8 @@ Production: `https://api.lightspark.com/grid/2025-10-13` ## Authentication -HTTP Basic Auth: `Authorization: Basic base64(clientId:clientSecret)` +- **Platform (Basic Auth):** `Authorization: Basic base64(clientId:clientSecret)` — used for most endpoints. +- **Agent (Agent Auth):** the `/agents/me/*` self-service endpoints accept an agent's own credentials. ## Platform Configuration @@ -21,7 +22,7 @@ HTTP Basic Auth: `Authorization: Basic base64(clientId:clientSecret)` | Method | Endpoint | Description | |--------|----------|-------------| -| GET | `/exchange-rates` | Get cached FX rates for payment corridors | +| GET | `/exchange-rates` | Get exchange rates | Query parameters: `sourceCurrency`, `destinationCurrency` (repeatable), `sendingAmount` (default 10000). Rates are cached ~5 minutes and include platform-specific fees. @@ -30,53 +31,118 @@ Rates are cached ~5 minutes and include platform-specific fees. | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/crypto/estimate-withdrawal-fee` | Estimate network + app fees for crypto withdrawal | +| POST | `/crypto/estimate-withdrawal-fee` | Estimate crypto withdrawal fee | + +## Discoveries + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/discoveries` | List available receiving institution names | ## Customers | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/customers` | Create a new customer | -| GET | `/customers` | List customers (paginated) | -| GET | `/customers/{customerId}` | Get customer details | -| PATCH | `/customers/{customerId}` | Update customer | -| DELETE | `/customers/{customerId}` | Delete customer | -| GET | `/customers/kyc-link` | Generate KYC link | -| POST | `/customers/bulk/csv` | Bulk upload customers | -| GET | `/customers/bulk/jobs/{jobId}` | Get bulk job status | +| POST | `/customers` | Add a new customer | +| GET | `/customers` | List customers | +| GET | `/customers/{customerId}` | Get customer by ID | +| PATCH | `/customers/{customerId}` | Update customer by ID | +| DELETE | `/customers/{customerId}` | Delete customer by ID | +| POST | `/customers/{customerId}/kyc-link` | Generate a hosted KYC link for an existing customer | +| POST | `/customers/{customerId}/verify-email` | Send an email verification code to a customer | +| POST | `/customers/{customerId}/verify-email/confirm` | Confirm a customer's email verification code | +| POST | `/customers/{customerId}/verify-phone` | Send a phone verification code to a customer | +| POST | `/customers/{customerId}/verify-phone/confirm` | Confirm a customer's phone verification code | +| POST | `/customers/bulk/csv` | Upload customers via CSV file | +| GET | `/customers/bulk/jobs/{jobId}` | Get bulk import job status | + +## Beneficial Owners + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/beneficial-owners` | Create a beneficial owner | +| GET | `/beneficial-owners` | List beneficial owners | +| GET | `/beneficial-owners/{beneficialOwnerId}` | Get a beneficial owner | +| PATCH | `/beneficial-owners/{beneficialOwnerId}` | Update a beneficial owner | + +## Documents + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/documents` | Upload a document | +| GET | `/documents` | List documents | +| GET | `/documents/{documentId}` | Get a document by ID | +| PUT | `/documents/{documentId}` | Replace a document | +| DELETE | `/documents/{documentId}` | Delete a document | + +## Verifications + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/verifications` | Submit customer for verification | +| GET | `/verifications` | List verifications | +| GET | `/verifications/{verificationId}` | Get a verification | ## Internal Accounts | Method | Endpoint | Description | |--------|----------|-------------| -| GET | `/customers/internal-accounts` | List customer internal accounts | +| GET | `/customers/internal-accounts` | List Customer internal accounts | | GET | `/platform/internal-accounts` | List platform internal accounts | +| PATCH | `/internal-accounts/{id}` | Update internal account | +| POST | `/internal-accounts/{id}/export` | Export internal account wallet credentials | Internal accounts are auto-created when customers are created based on platform config. -## External Accounts +## External Accounts (Customer) + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/customers/external-accounts` | List Customer external accounts | +| POST | `/customers/external-accounts` | Add a new external account | +| GET | `/customers/external-accounts/{externalAccountId}` | Get customer external account by ID | +| DELETE | `/customers/external-accounts/{externalAccountId}` | Delete customer external account by ID | +| POST | `/customers/external-accounts/{externalAccountId}/trust` | Start trusting a beneficiary | +| POST | `/customers/external-accounts/{externalAccountId}/trust/confirm` | Confirm trusting a beneficiary | +| POST | `/customers/external-accounts/{externalAccountId}/untrust` | Start untrusting a beneficiary | +| POST | `/customers/external-accounts/{externalAccountId}/untrust/confirm` | Confirm untrusting a beneficiary | + +## Platform External Accounts | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/customers/external-accounts` | Create customer external account | -| GET | `/customers/external-accounts` | List customer external accounts | | GET | `/platform/external-accounts` | List platform external accounts | +| POST | `/platform/external-accounts` | Add a new platform external account | +| GET | `/platform/external-accounts/{externalAccountId}` | Get platform external account by ID | +| DELETE | `/platform/external-accounts/{externalAccountId}` | Delete platform external account by ID | ## Same-Currency Transfers | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/transfer-in` | Transfer from external to internal (same currency) | -| POST | `/transfer-out` | Transfer from internal to external (same currency) | +| POST | `/transfer-in` | Create a transfer-in request (external to internal) | +| POST | `/transfer-out` | Create a transfer-out request (internal to external) | + +## Receiver Lookup + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/receiver/uma/{receiverUmaAddress}` | Look up an UMA address for payment | +| GET | `/receiver/external-account/{accountId}` | Look up an external account for payment | + +Returns: supported currencies, min/max amounts, required payer data fields. ## Cross-Currency Transfers (Quotes) | Method | Endpoint | Description | |--------|----------|-------------| | POST | `/quotes` | Create a transfer quote | -| GET | `/quotes` | List quotes (paginated) | -| GET | `/quotes/{quoteId}` | Get quote details | -| POST | `/quotes/{quoteId}/execute` | Execute a pending quote | +| GET | `/quotes/{quoteId}` | Get quote by ID | +| POST | `/quotes/{quoteId}/authorize` | Authorize a quote's SCA challenge | +| POST | `/quotes/{quoteId}/authorize/resend` | Resend a quote's SCA challenge code | +| POST | `/quotes/{quoteId}/execute` | Execute a quote | + +Quotes are created individually and fetched by ID; there is no list endpoint. ### Quote Source Types (`sourceType` discriminator) @@ -105,6 +171,7 @@ Internal accounts are auto-created when customers are created based on platform - `exchangeRate`: Units of sending currency per receiving currency unit - `feesIncluded`: Fees in smallest unit of sending currency - `paymentInstructions`: Funding options (for REALTIME_FUNDING sources) +- `scaChallenge`: Present while `status` is `PENDING_AUTHORIZATION` — the SCA challenge to authorize - `expiresAt`: Quote expiration timestamp - `transactionId`: Associated transaction ID - `rateDetails`: Detailed rate and fee breakdown @@ -112,28 +179,21 @@ Internal accounts are auto-created when customers are created based on platform ### Quote Statuses - `PENDING`: Awaiting execution +- `PENDING_AUTHORIZATION`: Awaiting Strong Customer Authentication. Occurs only for customers in an SCA-required region (e.g. EU); the quote carries an `scaChallenge` that must be authorized via `POST /quotes/{quoteId}/authorize` before execution. For realtime-funding sources, `paymentInstructions` are withheld until the challenge is satisfied. Authorization can be multi-step: if the quote is still `PENDING_AUTHORIZATION` after authorizing, authorize the next `scaChallenge` and repeat. - `PROCESSING`: Being executed - `COMPLETED`: Successfully completed - `FAILED`: Execution failed - `EXPIRED`: Quote expired before execution -## Receiver Lookup - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/receiver/uma/{receiverUmaAddress}` | Look up UMA address capabilities | -| GET | `/receiver/external-account/{accountId}` | Look up external account capabilities | - -Returns: supported currencies, min/max amounts, required payer data fields. - ## Transactions | Method | Endpoint | Description | |--------|----------|-------------| -| GET | `/transactions` | List transactions (paginated) | -| GET | `/transactions/{transactionId}` | Get transaction details | -| POST | `/transactions/{transactionId}/approve` | Approve incoming payment | -| POST | `/transactions/{transactionId}/reject` | Reject incoming payment | +| GET | `/transactions` | List transactions | +| GET | `/transactions/{transactionId}` | Get transaction by ID | +| POST | `/transactions/{transactionId}/confirm` | Confirm receipt delivery | +| POST | `/transactions/{transactionId}/approve` | Approve a pending incoming payment | +| POST | `/transactions/{transactionId}/reject` | Reject a pending incoming payment | ### Transaction Types @@ -144,6 +204,7 @@ Returns: supported currencies, min/max amounts, required payer data fields. - `CREATED`: Initial lookup has been created - `PENDING`: Quote has been created +- `PENDING_AUTHORIZATION`: Awaiting Strong Customer Authentication (SCA-required regions only) - `PROCESSING`: Funding received, payment initiated - `COMPLETED`: Payment sent to destination network - `REJECTED`: Receiving institution rejected payment, refunded @@ -151,11 +212,116 @@ Returns: supported currencies, min/max amounts, required payer data fields. - `REFUNDED`: Payment unable to complete, refunded - `EXPIRED`: Quote expired +## Auth (Embedded Wallet) + +Credentials, sessions, and delegated signing keys for embedded-wallet customers. + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/auth/credentials` | Create an authentication credential | +| GET | `/auth/credentials` | List authentication credentials | +| DELETE | `/auth/credentials/{id}` | Revoke an authentication credential | +| POST | `/auth/credentials/{id}/verify` | Verify an authentication credential | +| POST | `/auth/credentials/{id}/challenge` | Re-issue an authentication credential challenge | +| GET | `/auth/sessions` | List active sessions | +| DELETE | `/auth/sessions/{id}` | Revoke an authentication session | +| POST | `/auth/sessions/{id}/refresh` | Refresh an authentication session | +| POST | `/auth/delegated-keys` | Create a delegated signing key | +| GET | `/auth/delegated-keys` | List delegated signing keys | +| GET | `/auth/delegated-keys/{id}` | Get a delegated signing key | +| DELETE | `/auth/delegated-keys/{id}` | Revoke a delegated signing key | + +## SCA & 2FA + +Strong Customer Authentication factor enrollment, login, and two-factor reset. + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/sca/factors` | List enrolled SCA factors | +| POST | `/sca/factors` | Start SCA factor enrollment | +| POST | `/sca/factors/confirm` | Confirm SCA factor enrollment | +| DELETE | `/sca/factors/{credentialId}` | Delete an enrolled SCA factor | +| POST | `/sca/login/start` | Start an SCA login | +| POST | `/sca/login/complete` | Complete an SCA login | +| POST | `/sca/record-event` | Record a security event | +| POST | `/sca/factors/reset` | Start a 2FA reset | +| GET | `/sca/factors/reset/{resetId}` | Get 2FA reset status | +| POST | `/sca/factors/reset/{resetId}/complete` | Complete a 2FA reset | + +## Agents (Admin) + +Platform-managed agents and their approval workflow. + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/agents` | Create an agent | +| GET | `/agents` | List agents | +| GET | `/agents/approvals` | List agent transaction approval requests | +| GET | `/agents/{agentId}` | Get agent by ID | +| PATCH | `/agents/{agentId}` | Update agent | +| DELETE | `/agents/{agentId}` | Delete agent | +| PATCH | `/agents/{agentId}/policy` | Update agent policy | +| POST | `/agents/{agentId}/device-codes` | Regenerate a device code | +| POST | `/agents/{agentId}/actions/{actionId}/approve` | Approve an agent action | +| POST | `/agents/{agentId}/actions/{actionId}/reject` | Reject an agent action | +| GET | `/agents/device-codes/{code}/status` | Get device code status | +| POST | `/agents/device-codes/{code}/redeem` | Redeem device code | + +## Agents (Self-Service) + +The `/agents/me/*` endpoints are called by an agent using its own credentials (Agent Auth). + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/agents/me` | Get current agent | +| GET | `/agents/me/transactions` | List agent transactions | +| GET | `/agents/me/transactions/{transactionId}` | Get agent transaction by ID | +| POST | `/agents/me/quotes` | Create a transfer quote | +| GET | `/agents/me/quotes/{quoteId}` | Get agent quote by ID | +| POST | `/agents/me/quotes/{quoteId}/execute` | Execute a quote | +| GET | `/agents/me/actions` | List agent's own actions | +| GET | `/agents/me/actions/{actionId}` | Get an agent action | +| POST | `/agents/me/transfer-in` | Create a transfer-in | +| POST | `/agents/me/transfer-out` | Create a transfer-out | +| GET | `/agents/me/internal-accounts` | List agent's internal accounts | +| GET | `/agents/me/external-accounts` | List agent external accounts | +| POST | `/agents/me/external-accounts` | Add an external account | +| GET | `/agents/me/external-accounts/{externalAccountId}` | Get agent external account by ID | +| DELETE | `/agents/me/external-accounts/{externalAccountId}` | Delete agent external account | + +## Cards + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/cards` | Issue a card | +| GET | `/cards` | List cards | +| GET | `/cards/{id}` | Get a card | +| PATCH | `/cards/{id}` | Update a card (freeze/unfreeze, rebind funding sources, close) | +| POST | `/cards/{id}/reveal` | Reveal card details | + +### Sandbox Card Simulation + +Drive card lifecycle events in sandbox to test authorization, clearing, and returns. + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/sandbox/cards/{id}/simulate/authorization` | Simulate a card authorization | +| POST | `/sandbox/cards/{id}/simulate/clearing` | Simulate a card clearing | +| POST | `/sandbox/cards/{id}/simulate/return` | Simulate a card return | + +## Stablecoin Provider Accounts + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/stablecoin-provider-accounts` | Link a stablecoin provider account | +| GET | `/stablecoin-provider-accounts` | List stablecoin provider account links | +| GET | `/stablecoin-provider-accounts/{stablecoinProviderAccountId}` | Get a stablecoin provider account link | + ## Webhooks | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/sandbox/webhooks/test` | Send test webhook (sandbox only) | +| POST | `/sandbox/webhooks/test` | Send a test webhook (sandbox only) | ### Webhook Events @@ -163,6 +329,11 @@ Returns: supported currencies, min/max amounts, required payer data fields. - `outgoing-payment`: Payment status update - `customer-update`: Customer information or KYC status change - `internal-account-status`: Internal account status change +- `verification-update`: Verification status change +- `agent-action`: Agent action requires approval or changed status +- `card-state-change`: Card state changed +- `card-funding-source-change`: Card funding source changed +- `card-transaction`: Card transaction event - `bulk-upload`: Bulk job completion - `invitation-claimed`: Invitation claimed - `test-webhook`: Test event (sandbox only) @@ -171,19 +342,18 @@ Returns: supported currencies, min/max amounts, required payer data fields. | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/invitations` | Create invitation | -| GET | `/invitations` | List invitations | -| GET | `/invitations/{invitationCode}` | Get invitation | -| POST | `/invitations/{invitationCode}/claim` | Claim invitation | -| POST | `/invitations/{invitationCode}/cancel` | Cancel invitation | +| POST | `/invitations` | Create an UMA invitation | +| GET | `/invitations/{invitationCode}` | Get an UMA invitation by code | +| POST | `/invitations/{invitationCode}/claim` | Claim an UMA invitation | +| POST | `/invitations/{invitationCode}/cancel` | Cancel an UMA invitation | ## Sandbox Testing | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/sandbox/send` | Simulate sending payment | -| POST | `/sandbox/uma/receive` | Simulate receiving UMA payment | -| POST | `/sandbox/internal-accounts/{accountId}/fund` | Fund account in sandbox | +| POST | `/sandbox/send` | Simulate sending funds | +| POST | `/sandbox/uma/receive` | Simulate payment send to test receiving an UMA payment | +| POST | `/sandbox/internal-accounts/{accountId}/fund` | Simulate funding an internal account | ### Sandbox Account Patterns @@ -199,16 +369,16 @@ Use these account number endings for testing: | Method | Endpoint | Description | |--------|----------|-------------| -| POST | `/tokens` | Create API token | -| GET | `/tokens` | List API tokens | -| GET | `/tokens/{tokenId}` | Get token details | -| DELETE | `/tokens/{tokenId}` | Delete token | +| POST | `/tokens` | Create a new API token | +| GET | `/tokens` | List tokens | +| GET | `/tokens/{tokenId}` | Get API token by ID | +| DELETE | `/tokens/{tokenId}` | Delete API token by ID | ## UMA Providers | Method | Endpoint | Description | |--------|----------|-------------| -| GET | `/uma-providers` | List UMA provider domains | +| GET | `/uma-providers` | List available Counterparty Providers | ## Pagination @@ -224,11 +394,18 @@ List endpoints support cursor-based pagination: |------|-------------| | 200 | Success | | 201 | Created | +| 202 | Accepted (signed-retry challenge — resubmit with the returned proof) | +| 204 | No Content | | 400 | Bad request (invalid parameters) | | 401 | Unauthorized (invalid credentials) | +| 403 | Forbidden | | 404 | Not found | +| 405 | Method not allowed | | 409 | Conflict (duplicate) | +| 410 | Gone | | 412 | Precondition failed (UMA version) | +| 423 | Locked | | 424 | Counterparty issue | +| 429 | Too many requests (rate limited) | | 500 | Internal server error | | 501 | Not implemented | diff --git a/.claude/skills/grid-api/references/workflows.md b/.claude/skills/grid-api/references/workflows.md index 2b764a07d..750e18dac 100644 --- a/.claude/skills/grid-api/references/workflows.md +++ b/.claude/skills/grid-api/references/workflows.md @@ -19,10 +19,11 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ This returns: -- `id`: Lookup ID (use as `lookupId` when creating a quote — required for UMA destinations) -- Supported currencies -- Min/max amounts per currency -- Required payer data fields (`requiredPayerDataFields`) +- `lookupId`: Lookup identifier (pass as `lookupId` when creating a quote — required for UMA destinations) +- `receiverUmaAddress`: the UMA address that was looked up (UMA variant only) +- `supportedCurrencies[]`: each item has a nested `currency` (Currency object with code/decimals/name/symbol), + an `estimatedExchangeRate`, and `min`/`max` receivable amounts (smallest currency units) +- `requiredPayerDataFields[]`: payer fields the receiving institution requires before payment can complete 2. **Check sender's balance** @@ -58,6 +59,9 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "$GRID_BASE_URL/quotes" | jq . ``` +Optional top-level fields: `purposeOfPayment` (enum, e.g. `GOODS_OR_SERVICES` — required for some +corridors like India) and `remittanceInformation` (free-form memo, max 80 chars, travels on the rail). + The response includes: - `totalSendingAmount` and `sendingCurrency` @@ -80,6 +84,11 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ "$GRID_BASE_URL/quotes/Quote:xxx/execute" | jq . ``` +**SCA note (EU and other regulated regions):** execute may return the quote in +`PENDING_AUTHORIZATION` with an `scaChallenge` instead of initiating the transfer. Release it with +`POST /quotes/Quote:xxx/authorize` (body `{"code": "123456"}` in sandbox) — re-calling execute +returns 409. See the SCA subsection in `SKILL.md` for the full flow. Non-SCA customers skip this. + 6. **Monitor the transaction** ```bash @@ -188,8 +197,7 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ }, "destination": { "destinationType": "ACCOUNT", - "accountId": "ExternalAccount:xxx", - "currency": "MXN" + "accountId": "ExternalAccount:xxx" }, "lockedCurrencyAmount": 100000, "lockedCurrencySide": "RECEIVING" @@ -261,8 +269,7 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ }, "destination": { "destinationType": "ACCOUNT", - "accountId": "ExternalAccount:xxx", - "currency": "BTC" + "accountId": "ExternalAccount:xxx" }, "lockedCurrencyAmount": 10000000, "lockedCurrencySide": "SENDING", @@ -319,8 +326,7 @@ curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ }, "destination": { "destinationType": "ACCOUNT", - "accountId": "ExternalAccount:xxx", - "currency": "USD" + "accountId": "ExternalAccount:xxx" }, "lockedCurrencyAmount": 50000, "lockedCurrencySide": "RECEIVING" @@ -334,13 +340,16 @@ Use this when your platform receives an incoming payment that needs approval. ### Steps -1. **List pending incoming transactions** +1. **List incoming transactions** ```bash curl -s -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - "$GRID_BASE_URL/transactions?type=INCOMING&status=PENDING_APPROVAL" | jq . + "$GRID_BASE_URL/transactions?type=INCOMING" | jq . ``` +Incoming-payment approval is driven by the approval webhook (~5s window), not by polling a +status — there is no `PENDING_APPROVAL` transaction status to filter on. + 2. **Review transaction details** ```bash