OrbitDocs

Pull a billing cycle

POST /trigger-pull signs and submits pull_funds for a subscription.

POST/trigger-pull

Builds pull_funds(user, merchant), simulates it with prepareTransaction, signs it with the merchant key and submits it to Soroban Testnet. Once the transaction is confirmed, it sets next_billing_date to the pull time plus the plan's interval_seconds.

Testnet only. The merchant's secret seed travels in the request body. In production, sign on the client (Freighter) or with a KMS-held key.

Body

subscription_iduuidrequired

Subscription to bill. Must be a valid UUID.

merchant_secretstringrequired

Merchant's secret seed (S...). Must be a valid Stellar secret key and match the wallet of the plan's owner.

Request

curl -X POST http://localhost:3001/trigger-pull \
  -H "Content-Type: application/json" \
  -d '{ "subscription_id": "b2a9e4c1-...", "merchant_secret": "S..." }'

Response 200

{
  "message": "Successfully pulled funds on-chain!",
  "txHash": "6f4a8b...19e0"
}

Errors

StatusWhen
202Submitted but not confirmed before the poll timeout. next_billing_date is unchanged
400subscription_id is not a valid UUID, or merchant_secret is missing or is not a valid Stellar secret key
401Secret does not match the plan owner's wallet
404Subscription not found, or the plan's merchant no longer exists
500Simulation or submission failed, or the server is missing ORBIT_CONTRACT_ID. See contract errors
502The transaction failed on-chain. The subscription is marked past_due

How next_billing_date is calculated

When the pull is confirmed on-chain, the API sets:

next_billing_date = pull time + plan.interval_seconds
  • Pull time is the API server's clock at the moment it sees the confirmation, not the previous next_billing_date and not the ledger timestamp.
  • The interval is a fixed number of seconds, not a calendar month. See the "Intervals are fixed durations" note in Plans.
  • A late pull pushes every later cycle back by the same delay. The schedule does not catch up.
  • next_billing_date only moves on a 200. A 202 or 502 leaves it where it was.

Example. A plan with interval_seconds: 604800 (7 days) pulled at 2026-10-01T10:00:00Z gets next_billing_date 2026-10-08T10:00:00Z.

On a 202 the outcome is unknown. Poll getTransaction(txHash) before you mark the cycle as paid or retry the pull.

On this page