Last updated

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

AspectImmediateScheduled
RateLocked by your quote before you payLocked on the execution date — you don't know the source amount until then
RequestquoteId (+ optional onQuoteExpiry)scheduledDate + amount; no quoteId
First statusacceptedscheduled
onQuoteExpiryAppliesDoes 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.

StatuscodeMeaning
404payment_not_foundUnknown 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.
422not_cancelableThe 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 canceled payment.status.updated webhook is sent.
  • beneficiaryPaymentMethodId becomes null when you read the payment.
  • The quote stays spent — it can't fund another payment.
  • A pending approval workflow for the payment is removed.