Collect recurring payment
Manually starts a due active SDP-custody recurring payment collection, creating a linked payment transfer and collection attempt.
POST/Run in dashboard
If submission cannot be confirmed immediately, a 200 response returns the same transfer as processing with its known signature; reconciliation settles it, and the next due time advances only after exact on-chain confirmation.
Requires an API key with the recurring.execute action.
Path parameters
-
id·string· required · min length 1 · exampleprp_exampleSDP recurring payment record ID.
Headers
-
x-project-id·string· min length 1 · exampleprj_exampleRequired for dashboard sessions. API keys use their assigned project.
-
Idempotency-Key·string· requiredUse a unique 1–255 printable ASCII key per operation. Reuse it with the same request on retries. An in-flight attempt returns
409; a changed request returns422. Retry and recovery rules.
Responses
-
200Recurring payment collection result-
data·object· requiredRecurring payment collection response payload.
-
recurringPayment·object· requiredSDP-custody outbound recurring payment record.
-
id·string· required · exampleprp_exampleSDP recurring payment ID.
-
organizationId·string· required · min length 1 · exampleorg_exampleOrganization identifier.
-
projectId·string· required · min length 1 · exampleprj_exampleProject identifier.
-
sourceCustodyWalletId·string | null· required · examplecwlt_exampleExact source SDP Wallet ID, or null for unresolved legacy records.
-
sourceProviderWalletId·string· required · min length 1 · exampleprivy_wallet_123Source Provider wallet ID retained as evidence.
-
sourceAddress·string· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Source wallet address.
-
counterpartyId·string· required · examplecpty_exampleCounterparty ID.
-
counterpartyAccountId·string· required · examplecpacct_exampleCounterparty account ID.
-
destinationAddress·string· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Counterparty wallet owner address.
-
destinationTokenAccount·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Derived counterparty token account used for collection.
-
token·string· requiredSPL token mint address.
-
amount·string· required · example100.00Token amount in UI units (decimal string).
-
periodHours·integer· required · greater than 0 · example720 -
firstCollectionAt·string | null· required · formatdate-time· example2025-01-01T00:00:00.000ZRequested first collection timestamp.
-
nextCollectionDueAt·string | null· required · formatdate-time· example2025-01-01T00:00:00.000ZNext due collection timestamp.
-
planId·string | null· requiredLinked SDP subscription plan ID.
-
subscriptionId·string | null· requiredLinked SDP subscription ID.
-
planPda·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112On-chain plan PDA.
-
planCreatedAt·string | null· requiredOn-chain plan createdAt value as an unsigned integer string.
-
planCreationSignature·string | null· requiredSolana signature for plan creation.
-
subscriptionPda·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112On-chain subscription PDA.
-
subscriptionAuthorityAddress·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112On-chain subscription authority address.
-
authorizationSignature·string | null· requiredSolana signature for subscription authorization.
-
status·string· required · one ofpending_activation,activating,active,updating,canceling,resuming,paused,canceled,expired· exampleactiveRecurring payment status.
-
pausedReason·string | null· required · one ofcustody_policy_refused,failed_attempts· examplenullWhy SDP paused the recurring payment, set exactly while
statusispaused:custody_policy_refusedwhen the custody provider's policy refused to sign a collection,failed_attemptswhen one due period failed 5 times. Resume clears it. -
metadataUri·string | null· requiredOptional plan metadata URI.
-
createdBy·string | null· requiredCreator user ID or API key ID.
-
createdAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
updatedAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
collectionAttempt·object· requiredRecurring payment collection attempt record.
-
id·string· required · examplepsca_exampleCollection attempt ID.
-
organizationId·string· required · min length 1 · exampleorg_exampleOrganization identifier.
-
projectId·string· required · min length 1 · exampleprj_exampleProject identifier.
-
subscriptionId·string· requiredSubscription ID.
-
transferId·string | null· required · min length 1 · examplexfr_examplePayment transfer record ID when linked.
-
token·string· requiredToken mint or normalized token identifier.
-
amount·string· required · example100.00Token amount in UI units (decimal string).
-
dueAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
attemptedAt·string | null· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
status·string· required · one ofpending,processing,confirmed,failed,skipped· examplependingCollection attempt status.
-
signature·string | null· requiredSolana transaction signature.
-
error·string | null· requiredCollection error, if any.
-
metadata·object· requiredProvider/job metadata for the attempt.
One of:
-
source: "manual"
-
recurringPaymentId·string· required -
initiatedByKeyId·string | null· required -
source·string· required · alwaysmanual
-
-
source: "automated"
-
recurringPaymentId·string· required -
initiatedByKeyId·string | null· required -
source·string· required · alwaysautomated
-
-
source: "retry"
-
recurringPaymentId·string· required -
initiatedByKeyId·string | null· required -
source·string· required · alwaysretry -
initialSource·string· required · one ofmanual,automated -
transferId·string | null· required -
error·string· required -
retryAfterAt·string· required
-
-
source: "linked_transfer"
-
recurringPaymentId·string· required -
initiatedByKeyId·string | null· required -
source·string· required · alwayslinked_transfer -
initialSource·string· required · one ofmanual,automated -
transferId·string· required
-
-
-
createdAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
updatedAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
transfer·object· requiredTransfer transaction record.
-
fireblocks·objectNative Fireblocks transfer tracking. A processing payment may await approval in Fireblocks; network fees are paid by its source vault. A missing transaction ID means submission is still being reconciled. Do not create a replacement payment while its outcome is unknown.
-
transactionId·string | null· required -
status·string· required -
subStatus·string | null· required
-
-
id·string· required · min length 1 · examplexfr_exampleTransfer identifier (SDP record ID, not the on-chain signature).
-
organizationId·string· required · min length 1 · exampleorg_exampleOrganization identifier.
-
custodyWalletId·string | null· required · examplecwlt_exampleExact SDP Wallet ID associated with the persisted transfer, or null for unresolved legacy and observed rows.
-
providerWalletId·string· required · min length 1 · exampleprivy_wallet_123Provider wallet ID retained as evidence; not an execution selector.
-
projectId·string· min length 1 · exampleprj_exampleProject identifier for the transfer.
-
type·string· required · exampletransferTransfer type.
-
kind·string· requiredDashboard transaction kind derived from the transfer type and originating Payments resource.
-
direction·string· required · one ofinbound,outbound· exampleoutboundTransfer direction.
-
status·string· required · one ofpending,processing,confirmed,finalized,failed,awaiting_payment,settling,completed,canceled,expired· exampleconfirmedTransfer status.
-
signature·string | null· required · examplesig_exampleSolana transaction signature (tx id/hash).
-
serializedTx·string | null· required · min length 1 · examplebase64_tx_exampleBase64-encoded transaction payload, if available. Always null for transfer batch chunks, whose signed bytes stay server-side.
-
slot·integer | null· required · example123456Slot number, if confirmed.
-
blockTime·string | null· required · formatdate-time· example2025-01-01T00:00:00.000ZBlock time, if confirmed.
-
confirmedAt·string· formatdate-time· example2025-01-01T00:00:00.000ZWhen SDP confirmed the transfer. A ramp is confirmed once its delivery is verified onchain.
-
fee·integer | null· required · example5000Transaction fee in lamports.
-
error·string | null· required · exampleSignature failedError message if the transaction failed.
-
initiatedBy·objectInitiator that triggered the transfer.
-
type·string· required · one ofapi_key,user,system· exampleapi_keyInitiator type.
-
id·stringInitiator identifier if applicable.
-
display·stringHuman-friendly label for the initiator.
-
-
source·string· min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Source wallet address.
-
destination·string· min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Destination wallet address.
-
memo·string· max length 256Optional memo for the transfer.
-
rampsMemo·object· requiredFree-form key-value memo for offchain reconciliation of ramp transfers. At most 20 entries.
*·string· min length 1 · max length 256
-
token·stringToken symbol or mint address.
-
amount·string· example100.00Token amount in UI units (decimal string).
-
counterpartyId·string· examplecpty_exampleCounterparty tied to the transfer record, when available.
-
counterpartyDisplayName·string· exampleAcme StudioCurrent display name of the counterparty tied to the transfer.
-
providerReference·string· exampleramp_quote_exampleProvider quote or transaction reference used for ramp correlation.
-
deliveryMode·string· one ofhosted,manual_instructions,session_widget· examplesession_widgetRamp delivery mode. Hosted flows require the customer to complete a provider-hosted UI; manual instructions require the customer to fund displayed instructions; session widgets use a provider SDK session.
-
fiatCurrency·string· exampleUSDFiat currency for the ramp leg.
-
fiatAmount·string· example100.00Quoted fiat amount for the ramp leg: what the customer pays on an on-ramp, the payout quoted on an off-ramp. It never changes after the quote; an off-ramp's final payout is
fiatPayout. -
fiatPayout·objectWhat the provider reports paying out once it settles an off-ramp sale. Provider-reported: SDP cannot verify a bank payout.
-
amount·string· required · example20.82Fiat paid out, in display units. Differs from the quoted
fiatAmountwhen the provider re-prices the sale at receipt. -
currency·string· required · exampleUSDISO currency of the payout, as the provider reports it.
-
reportedAt·string· formatdate-time· example2025-01-02T00:00:00.000ZWhen the provider reported the payout.
-
-
deliveryMismatch·objectPresent while SDP holds a provider-settled ramp in
settlingbecause the chain contradicts the provider's delivery. A new provider report clears it.-
reason·string· required · one ofprovider_report_mismatch,transaction_failed,predates_transfer,not_delivered,amount_mismatch,transaction_not_found· exampleamount_mismatchWhy the chain contradicts the provider:
provider_report_mismatch(its report contradicts SDP's record),transaction_failed,predates_transfer(the transaction landed before the transfer),not_delivered(nothing of the quoted asset reached the quoted wallet),amount_mismatch, ortransaction_not_found(no finalized transaction an hour after the report; SDP keeps checking, and a later delivery completes the transfer). -
checkedAt·string· required · formatdate-time· example2025-01-02T00:00:00.000ZWhen SDP's delivery check reached this verdict.
-
reportedAmount·string· example20What the provider reported delivering, in display units.
-
observedAmount·string· example0.5What the chain shows the quoted wallet received, in display units.
-
-
risk·objectOptional risk evaluation for the transfer.
-
provider·string· required · exampletrmRisk scoring provider.
-
score·string· required · example0.12Provider-specific risk score.
-
level·string· required · one oflow,medium,high,unknown· examplelowRisk level classification.
-
evaluatedAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZTimestamp when risk was evaluated.
-
-
createdAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZCreation timestamp.
-
updatedAt·string· required · formatdate-time· example2025-01-02T00:00:00.000ZLast update timestamp.
-
-
-
meta·object· requiredResponse metadata.
-
requestId·string· required · min length 1 · examplereq_exampleRequest identifier for tracing.
-
timestamp·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
-
400Bad Request-
error·object· requiredStandard error payload.
-
code·string· requiredMachine-readable error code.
-
message·string· requiredHuman-readable error message.
-
details·objectOptional error details for debugging.
*·any
-
-
meta·object-
requestId·string· min length 1 · examplereq_exampleRequest identifier for tracing.
-
-
-
401UnauthorizedSame fields as
400. -
403Forbidden. Requires therecurring.executeaction: an API key must hold it (a module'smanageorexecutealso covers itsread), and a signed-in member's organization role must include it. Without it the error is INSUFFICIENT_PERMISSIONS anderror.details.actionnames the action. Any other 403 names its own reason.Same fields as
400. -
404Not FoundSame fields as
400. -
409ConflictSame fields as
400. -
422Unprocessable EntitySame fields as
400. -
500Internal Server ErrorSame fields as
400.
Example request
curl -X POST 'https://api.solana.com/v1/payments/recurring-payments/prp_example/collect' \
-H "Authorization: Bearer $SDP_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"Example responses
{
"data": {
"recurringPayment": {
"id": "prp_example",
"organizationId": "org_example",
"projectId": "prj_example",
"sourceCustodyWalletId": "cwlt_example",
"sourceProviderWalletId": "privy_wallet_123",
"sourceAddress": "So11111111111111111111111111111111111111112",
"counterpartyId": "cpty_example",
"counterpartyAccountId": "cpacct_example",
"destinationAddress": "So11111111111111111111111111111111111111112",
"destinationTokenAccount": "So11111111111111111111111111111111111111112",
"token": "example",
"amount": "100.00",
"periodHours": 720,
"firstCollectionAt": "2025-01-01T00:00:00.000Z",
"nextCollectionDueAt": "2025-01-01T00:00:00.000Z",
"planId": "plan_example",
"subscriptionId": "subscription_example",
"planPda": "So11111111111111111111111111111111111111112",
"planCreatedAt": "example",
"planCreationSignature": "example",
"subscriptionPda": "So11111111111111111111111111111111111111112",
"subscriptionAuthorityAddress": "So11111111111111111111111111111111111111112",
"authorizationSignature": "example",
"status": "active",
"pausedReason": null,
"metadataUri": "example",
"createdBy": "example",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
"collectionAttempt": {
"id": "psca_example",
"organizationId": "org_example",
"projectId": "prj_example",
"subscriptionId": "subscription_example",
"transferId": "xfr_example",
"token": "example",
"amount": "100.00",
"dueAt": "2025-01-01T00:00:00.000Z",
"attemptedAt": "2025-01-01T00:00:00.000Z",
"status": "pending",
"signature": "example",
"error": "example",
"metadata": {
"recurringPaymentId": "recurringpayment_example",
"initiatedByKeyId": "initiatedbykey_example",
"source": "manual"
},
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
"transfer": {
"fireblocks": {
"transactionId": "transaction_example",
"status": "example",
"subStatus": "example"
},
"id": "xfr_example",
"organizationId": "org_example",
"custodyWalletId": "cwlt_example",
"providerWalletId": "privy_wallet_123",
"projectId": "prj_example",
"type": "transfer",
"kind": "example",
"direction": "outbound",
"status": "confirmed",
"signature": "sig_example",
"serializedTx": "base64_tx_example",
"slot": 123456,
"blockTime": "2025-01-01T00:00:00.000Z",
"confirmedAt": "2025-01-01T00:00:00.000Z",
"fee": 5000,
"error": "Signature failed",
"initiatedBy": {
"type": "api_key"
},
"source": "So11111111111111111111111111111111111111112",
"destination": "So11111111111111111111111111111111111111112",
"rampsMemo": {
"invoice": "INV-123",
"po": "PO-9"
},
"amount": "100.00",
"counterpartyId": "cpty_example",
"counterpartyDisplayName": "Acme Studio",
"providerReference": "ramp_quote_example",
"deliveryMode": "session_widget",
"fiatCurrency": "USD",
"fiatAmount": "100.00",
"fiatPayout": {
"amount": "20.82",
"currency": "USD",
"reportedAt": "2025-01-02T00:00:00.000Z"
},
"deliveryMismatch": {
"reason": "amount_mismatch",
"checkedAt": "2025-01-02T00:00:00.000Z",
"reportedAmount": "20",
"observedAmount": "0.5"
},
"risk": {
"provider": "trm",
"score": "0.12",
"level": "low",
"evaluatedAt": "2025-01-01T00:00:00.000Z"
},
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-02T00:00:00.000Z"
}
},
"meta": {
"requestId": "req_example",
"timestamp": "2025-01-01T00:00:00.000Z"
}
}Error responses use the standard error shape.