{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-payments/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Send a payment","description":"Start today using our public API to power your next project."},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"send-a-payment"},"children":["Send a payment"]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Quote, then pay — with an idempotency key that makes duplicates impossible."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is the core flow, end to end: token → accounts → beneficiary → quote → payment. Every step below is runnable against sandbox."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"1.-get-an-access-token"},"children":["1. Get an access token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Credentials go in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization: Basic"]}," header, ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["not"]}," the body; your client certificate is required on this call as on every other. Request only the scopes you need."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST 'https://public-sandbox.tryjeeves.com/oauth/token' \\\n  --cert client.crt --key client.key \\\n  -H \"Authorization: Basic $(printf '%s' \"$CLIENT_ID:$CLIENT_SECRET\" | base64)\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d 'grant_type=client_credentials&scope=quotes:write payments:write payments:read beneficiaries:read beneficiaries:write accounts:read'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The response is a standard RFC 6749 token response, so any OAuth 2.0 client library can read it:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"access_token\": \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 300,\n  \"scope\": \"quotes:write payments:write payments:read beneficiaries:read beneficiaries:write accounts:read\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Read ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expires_in"]}," rather than hardcoding a lifetime — it's configured platform-side, and there is no refresh token. Check ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["scope"]}," too: it reports what was actually ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["granted"]},", and a scope that wasn't granted is dropped from the response rather than rejected at this step — you'd otherwise discover it as a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["403"]}," on your first real call."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["mTLS on every call:"]}," your client certificate is required on ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["every"]}," request, not just this one. The remaining examples omit ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--cert client.crt --key client.key"]}," for brevity — add them to each call."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"2.-register-the-beneficiary-and-get-their-ids"},"children":["2. Register the beneficiary and get their IDs"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The flow starts with who you pay. If the beneficiary already exists, fetch the IDs:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl 'https://public-sandbox.tryjeeves.com/v1/beneficiaries?page=1&limit=50' \\\n  -H \"Authorization: Bearer $JEEVES_ACCESS_TOKEN\"\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Take ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["beneficiaryId"]}," and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentMethods[].paymentMethodId"]}," for the rail you want (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["spei"]}," for the Mexican local rail, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wire"]}," for international wire). The beneficiary defines the corridor — Jeeves pays out to 190+ countries; USD→MXN here is just the worked example. Details and coverage: ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/payments/funding/beneficiaries"},"children":["Beneficiaries"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"creating-the-beneficiary-through-the-api"},"children":["Creating the beneficiary through the API"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If the payee doesn't exist yet, create it with three calls (all described in ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/payments/funding/beneficiaries#creating-beneficiaries"},"children":["Beneficiaries"]},"), using a token that carries ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["beneficiaries:write"]},":"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /v1/beneficiaries"]}," — create the person or business; you get a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["beneficiaryId"]}," and no payment methods yet."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /v1/beneficiary-schemas"]}," — read which ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentMethod"]}," values the corridor offers and which ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bankingInformation"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["contactInformation"]}," fields each one needs."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /v1/payment-methods"]}," — attach the method; the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentMethodId"]}," you get back is the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["beneficiaryPaymentMethodId"]}," of your payment."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"3.-pick-the-funding-account"},"children":["3. Pick the funding account"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl 'https://public-sandbox.tryjeeves.com/v1/product-accounts' \\\n  -H \"Authorization: Bearer $JEEVES_ACCESS_TOKEN\"\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Take ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["productAccountId"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["productAccountType"]}," from an ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["active"]}]}," account whose currency is what you'll pay from. Details: ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/payments/funding/product-accounts"},"children":["Funding accounts"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"4.-lock-the-rate"},"children":["4. Lock the rate"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amount"]}," is the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["destination"]}," amount — what the beneficiary receives. The source amount is derived and returned."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST 'https://public-sandbox.tryjeeves.com/v1/quotes' \\\n  -H \"Authorization: Bearer $JEEVES_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"sourceCurrency\": \"USD\",\n    \"destinationCurrency\": \"MXN\",\n    \"amount\": \"43035.00\",\n    \"productAccountType\": \"self-funded\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"quoteId\": \"9f2c1b84-3e7a-4d1f-9c05-8b6e2a4f7d13\",\n  \"rate\": \"17.2140\",\n  \"sourceCurrency\": \"USD\",\n  \"sourceAmount\": \"2500.00\",\n  \"destinationAmount\": \"43035.00\",\n  \"destinationCurrency\": \"MXN\",\n  \"createdAt\": \"2026-09-10T15:04:05Z\",\n  \"expiresAt\": \"2026-09-10T15:09:05Z\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The quote is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["single-use"]}," and expires at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expiresAt"]},". Full semantics: ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/payments/payment-flow/quotes-and-fx"},"children":["Quotes & FX"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"5.-create-the-payment"},"children":["5. Create the payment"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Idempotency-Key"]}," is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["required"]},": generate a fresh UUID per payment attempt and store it before sending."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST 'https://public-sandbox.tryjeeves.com/v1/payments' \\\n  -H \"Authorization: Bearer $JEEVES_ACCESS_TOKEN\" \\\n  -H \"Idempotency-Key: 1f8a6d90-4c37-4b52-9e81-0d5a7c2b6f34\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"quoteId\": \"9f2c1b84-3e7a-4d1f-9c05-8b6e2a4f7d13\",\n    \"onQuoteExpiry\": \"requote\",\n    \"productAccountId\": \"2b6f0c94-7a15-4e83-9d20-6c8e3b5a1f79\",\n    \"beneficiaryId\": \"3f1c9a52-6b7e-4d80-9a13-5c2e8f0b4d76\",\n    \"beneficiaryPaymentMethodId\": \"b7d2e4a1-8c05-49f3-a6b2-1e7d9c3f5024\",\n    \"purposeCode\": \"purchase_of_services\",\n    \"reference\": \"Invoice 2026-0912\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["reference"]}," is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["required"]},", 1–140 characters. An empty string is rejected exactly as omitting it is."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All checks run ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["before"]}," acceptance — quote freshness, beneficiary field patterns, purpose code, available funds. A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["422"]}," means nothing was created."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"the-idempotency-contract"},"children":["The idempotency contract"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"You do"},"children":["You do"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"You get"},"children":["You get"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Retry with the same key and same body"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["The ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["original result, byte for byte"]}," — never a second payment."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Reuse the key with a different body"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["409 idempotency_conflict"]},"; nothing created."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Timeout or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["5xx"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Retry with the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["same"]}," key. Generating a new key on retry is how duplicate payments happen."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Keys never expire; a key is permanently bound to its payment."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"requotes--the-one-rate-you-werent-shown"},"children":["Requotes — the one rate you weren't shown"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onQuoteExpiry"]}," decides what happens if the quote expired in flight. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fail"]}," (default) returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["422 quote_expired"]},". ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["requote"]}," prices a new quote at the current rate and uses it: the destination amount is unchanged, ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["the source amount moves"]},". The response then carries both ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["quoteId"]}," (the pricing quote) and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["originalQuoteId"]}," (the expired one). ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Compare them, and compare ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sourceAmount"]}," against what you expected"]}," — no separate event announces a requote."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"paymentId\": \"7a3e5f21-9d64-4c18-b0a7-1f52e8c3d9b6\",\n  \"status\": \"accepted\",\n  \"quoteId\": \"4c81de07-15b2-4a93-8f6d-2e7c09ab5431\",\n  \"originalQuoteId\": \"9f2c1b84-3e7a-4d1f-9c05-8b6e2a4f7d13\",\n  \"rate\": \"17.1980\",\n  \"sourceAmount\": \"2502.33\",\n  \"destinationAmount\": \"43035.00\",\n  \"createdAt\": \"2026-09-10T15:06:11Z\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"local-domestic-payments"},"children":["Local (domestic) payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The same flow handles payments where the source and destination currency are the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["same"]}," — nothing about idempotency, statuses, or webhooks changes. Example: paying a Mexican business from your ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["MXN Bill Pay credit"]}," account (reported as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["jeeves-pay-credit"]},", sent as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["credit-line"]},"), delivered in MXN via SPEI:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST 'https://public-sandbox.tryjeeves.com/v1/quotes' \\\n  -H \"Authorization: Bearer $JEEVES_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"sourceCurrency\": \"MXN\",\n    \"destinationCurrency\": \"MXN\",\n    \"amount\": \"43035.00\",\n    \"productAccountType\": \"credit-line\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Then create the payment against the quote exactly as above, using the beneficiary's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["spei"]}," payment method. The purpose-code corridor depends on the account type, so take the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["purposeCode"]}," from the list for your corridor (see ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#purpose-codes"},"children":["Purpose codes"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"purpose-codes"},"children":["Purpose codes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["purposeCode"]}," is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["mandatory on every payment"]},", never defaulted — and it is a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["string identifier"]}," naming why the payment is made (e.g. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["purchase_of_services"]},"), never a number. Which identifiers are valid depends on the corridor: the currency you pay from, the currency and country you pay to, and the kind of account funding the payment."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Read the exact list for your corridor from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["purposeCodes[]"]}," in the response of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /v1/beneficiary-schemas"]}," and send one of its ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["code"]}," values. That list is the one payment-time validation accepts. See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/payments/funding/beneficiaries#2-ask-what-the-corridor-needs"},"children":["Beneficiaries"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Omitting ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["purposeCode"]}," returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["400"]}," (field listed in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["errors"]},"); an identifier outside the corridor's list returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["422 validation_failed"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"6.-track-it"},"children":["6. Track it"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payment.status.updated"]}," webhook fires on every status change, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /v1/payments/{paymentId}"]}," is the authoritative state. See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/payments/payment-flow/tracking-payments"},"children":["Tracking & webhooks"]},"."]}]},"headings":[{"value":"Send a payment","id":"send-a-payment","depth":1},{"value":"1. Get an access token","id":"1.-get-an-access-token","depth":2},{"value":"2. Register the beneficiary and get their IDs","id":"2.-register-the-beneficiary-and-get-their-ids","depth":2},{"value":"Creating the beneficiary through the API","id":"creating-the-beneficiary-through-the-api","depth":3},{"value":"3. Pick the funding account","id":"3.-pick-the-funding-account","depth":2},{"value":"4. Lock the rate","id":"4.-lock-the-rate","depth":2},{"value":"5. Create the payment","id":"5.-create-the-payment","depth":2},{"value":"The idempotency contract","id":"the-idempotency-contract","depth":3},{"value":"Requotes — the one rate you weren't shown","id":"requotes--the-one-rate-you-werent-shown","depth":3},{"value":"Local (domestic) payments","id":"local-domestic-payments","depth":2},{"value":"Purpose codes","id":"purpose-codes","depth":2},{"value":"6. Track it","id":"6.-track-it","depth":2}],"frontmatter":{"seo":{"title":"Send a payment"}},"lastModified":"2026-11-04T03:42:09.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/payments/payment-flow/send-payment","userData":{"isAuthenticated":false,"teams":["anonymous"]}}