Skip to main content
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

The SDP host exposes only operational surfaces (/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:
The session is an HS256 JWT carrying 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.
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
Only Stellar (SEP-10) sessions can call POST /transfer. A Privy session gets a 501 on that route. /transfer only fee-bumps a transaction whose source is your own session account (otherwise 403).

POST /transfer

The live non-custodial write path. You build and sign a confidential_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.
The endpoint accepts { signedXDR } and fee-bumps it.

Error shape

Every handled error returns the same shape with an appropriate status code:
(Unknown routes fall through to the framework’s default { 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 /transfer requires a Stellar (SEP-10) session and accepts { signedXDR }.
  • POST /withdraw and POST /auditor/disclose return 501 because those operations run client-side, where you hold both keys.

FAQ

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.
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.
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.