curl --request POST \
--url https://api.platform.dakota.xyz/scheduled-payments \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--header 'x-idempotency-key: <x-idempotency-key>' \
--data '
{
"signer_id": "2vWxSigner000000000000000000",
"wallet_id": "2vWxWallet000000000000000000",
"destination_id": "2vWxDestination00000000000000",
"amount": "10000",
"asset": "USDC",
"dates": [
1781481600
]
}
'const options = {
method: 'POST',
headers: {
'x-idempotency-key': '<x-idempotency-key>',
'x-api-key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
signer_id: '2vWxSigner000000000000000000',
wallet_id: '2vWxWallet000000000000000000',
destination_id: '2vWxDestination00000000000000',
amount: '10000',
asset: 'USDC',
dates: [1781481600]
})
};
fetch('https://api.platform.dakota.xyz/scheduled-payments', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.platform.dakota.xyz/scheduled-payments"
payload = {
"signer_id": "2vWxSigner000000000000000000",
"wallet_id": "2vWxWallet000000000000000000",
"destination_id": "2vWxDestination00000000000000",
"amount": "10000",
"asset": "USDC",
"dates": [1781481600]
}
headers = {
"x-idempotency-key": "<x-idempotency-key>",
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.platform.dakota.xyz/scheduled-payments"
payload := strings.NewReader("{\n \"signer_id\": \"2vWxSigner000000000000000000\",\n \"wallet_id\": \"2vWxWallet000000000000000000\",\n \"destination_id\": \"2vWxDestination00000000000000\",\n \"amount\": \"10000\",\n \"asset\": \"USDC\",\n \"dates\": [\n 1781481600\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-idempotency-key", "<x-idempotency-key>")
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}[
{
"id": "2vWxScheduled000000000000000",
"signer_id": "2vWxSigner000000000000000000",
"wallet_id": "2vWxWallet000000000000000000",
"amount": "10000",
"asset": "USDC",
"address": "0xa11ce00000000000000000000000000000000001",
"network_id": "base-sepolia",
"status": "scheduled",
"scheduled_at": 1781481600
}
]{
"type": "https://docs.dakota.xyz/api-reference/errors#invalid-request",
"title": "Invalid Request",
"status": 400,
"detail": "signer is not permitted to spend on wallet"
}{
"type": "https://docs.dakota.xyz/api-reference/errors#account-under-review",
"title": "Account Under Review",
"status": 403,
"detail": "This account is under review and cannot move funds until the review is resolved."
}{
"type": "https://docs.dakota.xyz/api-reference/errors#not-found",
"title": "Not Found",
"status": 404,
"detail": "wallet not found"
}{
"type": "https://docs.dakota.xyz/api-reference/errors#not-found",
"title": "Customer Not Found",
"status": 404,
"detail": "Customer cst_2abc123 was not found in your organization.",
"instance": "https://api.platform.dakota.xyz/customers/cst_2abc123",
"request_id": "req_7f3a8b2c"
}Schedule a payment directly (BETA)
Beta — early access.
Creates one or more scheduled payments for a signer WITHOUT the proposal flow — schedule directly under an existing active mandate (coverage is matched at fire time, so no new signature is needed here). The payments bind the given signer and funding wallet. The signer must be permitted to spend on that wallet, the wallet must belong to the calling client, and the destination must be a crypto destination of the wallet’s customer. The schedule is explicit dates (one payment per timestamp) OR count × interval_seconds from start_at (0 ⇒ now); dates are unix SECONDS and are rejected if in the past or implausibly far ahead. Whether a mandate covers each payment is decided at fire time by the money-path gate — a scheduled payment with no covering active mandate fails at fire, it is not rejected here.
curl --request POST \
--url https://api.platform.dakota.xyz/scheduled-payments \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--header 'x-idempotency-key: <x-idempotency-key>' \
--data '
{
"signer_id": "2vWxSigner000000000000000000",
"wallet_id": "2vWxWallet000000000000000000",
"destination_id": "2vWxDestination00000000000000",
"amount": "10000",
"asset": "USDC",
"dates": [
1781481600
]
}
'const options = {
method: 'POST',
headers: {
'x-idempotency-key': '<x-idempotency-key>',
'x-api-key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
signer_id: '2vWxSigner000000000000000000',
wallet_id: '2vWxWallet000000000000000000',
destination_id: '2vWxDestination00000000000000',
amount: '10000',
asset: 'USDC',
dates: [1781481600]
})
};
fetch('https://api.platform.dakota.xyz/scheduled-payments', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.platform.dakota.xyz/scheduled-payments"
payload = {
"signer_id": "2vWxSigner000000000000000000",
"wallet_id": "2vWxWallet000000000000000000",
"destination_id": "2vWxDestination00000000000000",
"amount": "10000",
"asset": "USDC",
"dates": [1781481600]
}
headers = {
"x-idempotency-key": "<x-idempotency-key>",
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.platform.dakota.xyz/scheduled-payments"
payload := strings.NewReader("{\n \"signer_id\": \"2vWxSigner000000000000000000\",\n \"wallet_id\": \"2vWxWallet000000000000000000\",\n \"destination_id\": \"2vWxDestination00000000000000\",\n \"amount\": \"10000\",\n \"asset\": \"USDC\",\n \"dates\": [\n 1781481600\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-idempotency-key", "<x-idempotency-key>")
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}[
{
"id": "2vWxScheduled000000000000000",
"signer_id": "2vWxSigner000000000000000000",
"wallet_id": "2vWxWallet000000000000000000",
"amount": "10000",
"asset": "USDC",
"address": "0xa11ce00000000000000000000000000000000001",
"network_id": "base-sepolia",
"status": "scheduled",
"scheduled_at": 1781481600
}
]{
"type": "https://docs.dakota.xyz/api-reference/errors#invalid-request",
"title": "Invalid Request",
"status": 400,
"detail": "signer is not permitted to spend on wallet"
}{
"type": "https://docs.dakota.xyz/api-reference/errors#account-under-review",
"title": "Account Under Review",
"status": 403,
"detail": "This account is under review and cannot move funds until the review is resolved."
}{
"type": "https://docs.dakota.xyz/api-reference/errors#not-found",
"title": "Not Found",
"status": 404,
"detail": "wallet not found"
}{
"type": "https://docs.dakota.xyz/api-reference/errors#not-found",
"title": "Customer Not Found",
"status": 404,
"detail": "Customer cst_2abc123 was not found in your organization.",
"instance": "https://api.platform.dakota.xyz/customers/cst_2abc123",
"request_id": "req_7f3a8b2c"
}Authorizations
Headers
Unique key (UUID) to ensure request idempotency on POST, PUT and PATCH requests.
A key is scoped to your client, the HTTP method and the path, and is remembered for 24 hours after a
successful (below 400) response. Repeating the same request (same JSON body, whatever the key order or
whitespace) with the same key within that window returns the original response with
X-Idempotent-Replayed: true instead of executing the request again. Using the same key on the same
endpoint with a different body returns 422 with problem type idempotency-key-reused; the request is
not executed. Error responses (4xx and 5xx) are not remembered, so a request refused for a fixable reason
can be corrected and retried under the same key. Request bodies that are not JSON (for example file
uploads) are not compared.
Body
Direct (signer-first) schedule create. The signer + funding wallet are the caller's choice; the schedule is explicit dates, or count × interval_seconds from start_at. Name the payee EITHER with destination_id (an existing crypto destination of the wallet's customer) OR with a direct address + network_id (a raw crypto address, no recipient — matched on the address at fire time, like a mandate address target) — exactly one of the two.
The signer the payments bind to; its hosted agent signs at fire time.
The funding wallet these payments spend from. Required — the signer must be permitted to spend on it, and its chain family must match the payment network.
Decimal string, at the destination asset.
An existing crypto destination of the wallet's customer to pay. Omit to pay a direct address (address + network_id).
A direct crypto address to pay — an alternative to destination_id (no recipient). Requires network_id; the wallet's chain family must match.
The network for a direct-address payment (required together with address).
Explicit per-payment unix times (one payment each). Win over count × interval_seconds.
366Number of payments when using count × interval_seconds (default 1).
x <= 366Cadence in seconds between payments when using count.
First due time (0 ⇒ now).
Response
The scheduled payments that were created (one row per date)
The signer (hosted agent) this payment fires under.
The funding wallet this payment spends from (the customer's choice at acceptance).
AUDIT — the mandate that covered this payment at fire time. Absent until the payment executes (coverage is decided at fire time, not bound at rest).
AUDIT — WHICH version of that mandate authorized this payment. A mandate's rule can be amended (new versions are appended), so mandate_id alone no longer identifies the caps the payment was judged against; this does, and it stays true after later amendments. Look the rule up at GET /mandates/{mandate_id}/versions. Always accompanies mandate_id — when the covering mandate could not be determined, both are absent rather than one being guessed.
AUDIT — the money-path transaction created when this payment fired. Absent until the payment executes.
What LEAVES the funding wallet, denominated in asset on network_id. For a DIRECT payment that is also what the payee receives. For a CONVERTED payment (the payee settles on a different asset or chain) it is the DEPOSIT into the conversion account, grossed up so the payee nets the requested figure — so it is LARGER than the payout and in a DIFFERENT asset. Never render it against output_asset.
The asset amount is denominated in — the asset that LEAVES the wallet. For a converted payment this is the deposit asset, not the one the payee receives (see output_asset).
Why the payment failed (e.g. the mandate gate's denied dimensions); absent unless status is failed.
The recipient this payment pays, when it was created from a destination. Absent for a direct-address payment (which carries no recipient).
The REAL destination this payment settles to — a bank (for an offramp) or a crypto destination. For an auto-account (convert-and-forward) payment, address is the crypto DEPOSIT actually paid while this names the bank/crypto target, so a client can show which account the scheduled payment is for. Absent for a direct-address payment.
The kind of the REAL destination (see destination_id). bank marks an offramp (the payment converts crypto to fiat and forwards via a bank rail); crypto a direct or cross-family crypto payout. Absent for a direct-address payment with no destination.
bank, crypto A human label for the real destination — e.g. "Chase ••••5432" for a bank, or a shortened address for crypto — so a client can show which account the payment settles to without a second lookup. Absent when unresolved.
For a bank destination, the payout rail (e.g. ach, fedwire, swift). Absent for crypto.
The asset the recipient ULTIMATELY receives. For an offramp this is the fiat currency (e.g. USD) the deposit converts to; for a direct crypto payment it equals asset. DO NOT PAIR THIS WITH amount: for a converted payment amount is the DEPOSIT — what leaves the wallet, in the deposit's own asset — and it is grossed up so the payee nets the requested figure, so a real payment reads amount 0.211112 and pays out 0.19. The rate is 1:1; the fee is not. This response carries no output amount, so a converted payment's payout figure is not available here.
For a crypto destination, the network the recipient ULTIMATELY receives on — for a cross-family swap this differs from network_id (the DEPOSIT network the wallet pays), e.g. output_network solana-devnet while network_id is base-sepolia. Equals network_id for a direct crypto payment; absent for a bank offramp (fiat has no network).
scheduled, cancelled, executed, failed Was this page helpful?

