payroll-api is a Fastify BFF (backend-for-frontend): a public-but-authenticated surface
the browser SPA calls. It holds Confiroll’s operational credentials internally and never
exposes a database or a user key.
Interactive endpoint reference
The Core endpoints and Payroll resources groups in this tab document every
endpoint below, with request/response schemas and a request playground, generated from
openapi.yaml.Base URLs
/health, /.well-known/stellar.toml); it
is not part of the payroll API. Every payroll route lives under https://api.confiroll.com.
Endpoints at a glance
Every route the live build serves.Auth marks whether a Bearer session JWT is required.
The interactive playground for every endpoint is under the Core endpoints and Payroll
resources groups in this tab. Use it to inspect schemas and fire real requests against
https://api.confiroll.com.Authentication
Protected routes require a Bearer session JWT:sub, kind, and an expiry (default 1 hour). You
obtain one of two ways, both returning { session, kind, sub }. Store session and send it as
the Bearer token on every protected call.
- SEP-10 (wallet)
- Privy (email)
A challenge-response handshake proves you control a Stellar account. The resulting session
has
kind: "stellar" and sub set to your G-address. This is the session that can call
POST /transfer.1
Request a challenge
Ask for a challenge transaction for your account. The challenge is single-use and
expires in 5 minutes.
Response
2
Sign the challenge with your wallet
Sign the returned
transaction with signTransaction from Stellar Wallets Kit. You
sign the envelope only; no key leaves the wallet.3
Verify and receive a session
Post the signed XDR back. The API validates the signature and issues the session.
POST /auth/sep10/verify
Response
POST /transfer
The live non-custodial write path. You build and sign aconfidential_transfer in the browser
(employer as tx source, Fork B), then hand the API only the signed inner envelope. The API binds
tx.source == session.sub, then the sponsor fee-bumps it (outer fee = inner fee times 2) and
submits. On-chain the fee_account is the sponsor and your XLM delta is 0. Details in
Fee sponsorship.
Response
string
The on-chain transaction hash of the submitted fee-bump. Look it up on stellar.expert testnet.
string
SUCCESS once the sponsor polls the submission to completion over Soroban RPC.{ signedXDR } and fee-bumps it.
Error shape
Every handled error returns the same shape with an appropriate status code:{ statusCode, error, message }
404.) The API limits request bodies to 256 KiB.
Endpoints
Core endpoints
GET /health, the auth calls (/auth/sep10/challenge, /auth/sep10/verify,
/auth/privy), POST /transfer, POST /batch with GET /batch/{jobId}, POST /withdraw,
and POST /auditor/disclose.Payroll resources
The SDP-aligned data API:
GET /me, /contractors*, /batches*, /payouts, and
/funding/*.Good to know:
POST /transferrequires a Stellar (SEP-10) session and accepts{ signedXDR }.POST /withdrawandPOST /auditor/disclosereturn501because those operations run client-side, where you hold both keys.
FAQ
Which session type do I need for POST /transfer?
Which session type do I need for POST /transfer?
A Stellar (SEP-10) session.
/transfer binds the inner transaction’s source to your
session account, and only a SEP-10 session carries a G-address as its sub. A Privy
(email) session returns 501 on that route. Sign in with a wallet to run confidential
transfers.Why did my transfer return 403 instead of 401?
Why did my transfer return 403 instead of 401?
401 means the request had no valid session. 403 means the session is valid but the inner
transaction’s source is not your session account. The API refuses to fee-bump anyone
else’s transaction, so build the inner transaction with your own account as the source.What is the difference between a 422 and a 501 on /transfer?
What is the difference between a 422 and a 501 on /transfer?
A
501 on /transfer means the session kind is wrong (a Privy session). A 422 means the
session and binding were fine but the sponsor refused: the target contract is off the
allow-list, the inner fee is over the cap, or the per-account quota is exhausted. See
Fee sponsorship for the guard-rails.