Scheduled payments
Future-dated payments, priced on the day they execute.
Set scheduledDate to a future date and the payment enters scheduled — accepted and queued, with the FX rate locked on the execution date, not at creation.
The shape
Omit quoteId and send amount (the destination amount) instead — quoteId and scheduledDate are mutually exclusive, and violating that returns 422 invalid_schedule.
curl -X POST 'https://public-sandbox.tryjeeves.com/v1/payments' \
-H "Authorization: Bearer $JEEVES_ACCESS_TOKEN" \
-H "Idempotency-Key: 6b1e2f47-9c30-4a85-b7d2-3e8f0a5c1d94" \
-H "Content-Type: application/json" \
-d '{
"scheduledDate": "<a future date, YYYY-MM-DD>",
"amount": "43035.00",
"productAccountId": "2b6f0c94-7a15-4e83-9d20-6c8e3b5a1f79",
"beneficiaryId": "3f1c9a52-6b7e-4d80-9a13-5c2e8f0b4d76",
"beneficiaryPaymentMethodId": "b7d2e4a1-8c05-49f3-a6b2-1e7d9c3f5024",
"purposeCode": "purchase_of_services",
"reference": "Invoice 2026-0912"
}'Replace scheduledDate with a date in the future, written YYYY-MM-DD. reference is required on every payment, scheduled or not — leaving it out returns 400.
What's different from an immediate payment
| Aspect | Immediate | Scheduled |
|---|---|---|
| Rate | Locked by your quote before you pay | Locked on the execution date — you don't know the source amount until then |
| Request | quoteId (+ optional onQuoteExpiry) | scheduledDate + amount; no quoteId |
| First status | accepted | scheduled |
onQuoteExpiry | Applies | Does not apply |
Everything else is identical: the Idempotency-Key is required and behaves the same, purposeCode is mandatory with the same corridor rules, and validation runs before acceptance.
Tracking a scheduled payment
The payment sits in scheduled until the execution date, then moves through the normal lifecycle (in-progress → completed, or a failure state). Each transition arrives as a payment.status.updated webhook; GET /v1/payments/{paymentId} shows scheduledDate and the current status at any time.
Because the rate locks at execution, review the payment's rate and sourceAmount once it leaves scheduled — that's when you learn what the payment actually cost.
Cancelling a payment
POST /v1/payments/{paymentId}/cancel — scope payments:write. There is no request body.
curl -X POST 'https://public-sandbox.tryjeeves.com/v1/payments/7a3e5f21-9d64-4c18-b0a7-1f52e8c3d9b6/cancel' \
-H "Authorization: Bearer $JEEVES_ACCESS_TOKEN"{
"paymentId": "7a3e5f21-9d64-4c18-b0a7-1f52e8c3d9b6",
"status": "canceled",
"canceledAt": "2026-11-02T16:20:45Z"
}When you can cancel. Only while the status is in-review or scheduled — including a payment held by an approval policy. An accepted payment is released too quickly to cancel, and a payment that is in-progress or later cannot be cancelled.
| Status | code | Meaning |
|---|---|---|
| 404 | payment_not_found | Unknown paymentId, an id that isn't a UUID, a payment that belongs to another customer, or a payment that wasn't created through the API. |
| 422 | not_cancelable | The payment isn't in a status that can be cancelled. This includes a payment that was already cancelled. |
This call is not idempotent. A retry returns 422 not_cancelable because the first call succeeded. After a timeout, read GET /v1/payments/{paymentId} to see whether the payment is canceled before you retry.
What a cancel does:
- A
canceledpayment.status.updatedwebhook is sent. beneficiaryPaymentMethodIdbecomesnullwhen you read the payment.- The quote stays spent — it can't fund another payment.
- A pending approval workflow for the payment is removed.