Builder Backend API Reference
Base URL: https://builders.sweepster.xyz
The builder-backend manages builder registration, attribution, the accrual ledger, and the SIWE-authenticated builder dashboard.
Endpoint groups:
- Self-signup — public, SIWE-gated:
POST /builders/self - Dashboard auth — SIWE login + session:
GET /auth/nonce,POST /auth/verify,POST /auth/logout - Dashboard data — session-cookie auth:
GET /me,GET /me/accruals,GET /me/activity,POST /me/apikey/rotate - Server-to-server — API-key auth:
POST /attribute - Admin / machine —
POST /builders,POST /accruals,GET /builders/:id/accruals(admin Bearer token); the/admin/*console +POST /revenueroutes are internal
POST /builders/self
Public, SIWE-gated builder self-signup. Creates a zero-commission builder owned by the signing wallet, issues an API key (shown once), and starts a dashboard session. This is the endpoint behind the dashboard "Create a builder account" flow.
Auth: none (a valid SIWE signature over a fresh nonce is the gate)
Gated by: the SELF_SIGNUP_ENABLED backend flag — returns 403 signup_disabled when off.
Flow
GET /auth/nonce→{ nonce }- Build a SIWE message embedding that nonce (domain = the dashboard host) and have the wallet sign it.
POST /builders/selfwith the message, signature, and a display name.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | The SIWE message the wallet signed (embeds the issued nonce) |
signature | string | Yes | The wallet's signature over message |
name | string (1–64) | Yes | Display name for the builder |
Response 201
{
"builder": {
"id": "bld_f39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"name": "Acme Wallet",
"builderBps": 0,
"maxBuilderBps": 0,
"payoutAddress": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"createdAtMs": 1717340400000
},
"apiKey": "sk_live_..."
}A Set-Cookie session header is also returned, so the caller is logged into the dashboard immediately. The builder id is the deterministic bld_<lowercased payout address>. builderBps/maxBuilderBps are 0 — contact Sweepster to raise the cap.
The
apiKeyis shown only once. Save it immediately; it cannot be retrieved again (you can rotate it viaPOST /me/apikey/rotate).
Errors
| Status | Body | Cause |
|---|---|---|
| 403 | { "error": "signup_disabled" } | SELF_SIGNUP_ENABLED is off |
| 422 | { "error": "bad_request" } | Missing/invalid body (name length, etc.) |
| 401 | { "error": "bad_nonce" } | Nonce missing, unknown, or already consumed |
| 401 | { "error": "bad_signature" } | SIWE signature did not recover the expected signer/domain |
| 409 | { "error": "already_registered" } | This wallet already owns a builder — sign in instead |
| 429 | Rate limit | Too many signup attempts |
Dashboard auth (SIWE)
These endpoints power wallet sign-in for the dashboard. They use credentialed CORS for the dashboard origin and deliver an httpOnly, Secure, cross-subdomain session cookie.
GET /auth/nonce
Issue a single-use SIWE nonce.
Auth: none
Response 200: { "nonce": "ab12cd34..." }
POST /auth/verify
Verify a SIWE login. Recovers the signer, matches it to a registered builder's payoutAddress, and sets the session cookie.
Auth: none (the SIWE signature is the credential)
Request body
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | The signed SIWE message (embeds the nonce from GET /auth/nonce) |
signature | string | Yes | The wallet's signature over message |
Response 200
{ "builder": { "id": "bld_acme", "name": "Acme Wallet", "builderBps": 0, "maxBuilderBps": 0, "payoutAddress": "0x..." } }A Set-Cookie session header is returned alongside.
Errors
| Status | Body | Cause |
|---|---|---|
| 422 | { "error": "bad_request" } | Invalid body |
| 401 | { "error": "bad_nonce" } | Nonce missing/unknown/consumed |
| 401 | { "error": "bad_signature" } | Signature did not verify |
| 404 | { "error": "no_builder" } | Signer is not a registered builder (offer self-signup) |
POST /auth/logout
Clear the session cookie.
Auth: session cookie
Response 200: { "ok": true }
Dashboard data (/me)
Builder-facing read + key-rotation endpoints authenticated by the session cookie from SIWE login. These power the dashboard panels. All return 401 unauthorized if the session is missing or expired.
GET /me
The authenticated builder's account + config (read-only).
{ "id": "bld_acme", "name": "Acme Wallet", "builderBps": 0, "maxBuilderBps": 0, "payoutAddress": "0x...", "createdAtMs": 1717340400000 }GET /me/accruals
Payable balances + lifetime totals, per token.
{
"balances": [{ "token": "0xUSDC", "amount": "3000000" }],
"lifetime": [{ "token": "0xUSDC", "amount": "9000000" }]
}GET /me/activity
Per-sweep accrual ledger for the authenticated builder.
{
"entries": [
{ "sweepId": "0xTxHash_3", "token": "0xUSDC", "amount": "3000000", "paid": false, "createdAtMs": 1717340400000 }
]
}POST /me/apikey/rotate
Generate a new API key. The previous key is invalidated immediately and the new key is shown once.
Request body: (none)
Response 200: { "apiKey": "sk_live_..." }
POST /builders
Register a new builder. Admin-gated. (Self-serve integrators should use POST /builders/self instead.)
Auth: Authorization: Bearer <ADMIN_TOKEN>
Request body
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique builder identifier (e.g. "bld_acme") |
name | string | Yes | Human-readable builder name |
builderBps | number (integer ≥ 0) | Yes | Builder markup in basis points (e.g. 30 = 0.30%) |
maxBuilderBps | number (integer ≥ 0) | Yes | Hard cap on builder markup |
payoutAddress | string (hex address) | Yes | Address for accrual payouts (0x...) |
Response 201
{
"builder": {
"id": "bld_acme",
"name": "Acme Wallet",
"builderBps": 30,
"maxBuilderBps": 50,
"payoutAddress": "0xPayoutAddress",
"createdAtMs": 1717340400000
},
"apiKey": "sk_live_..."
}The
apiKeyis returned only once. Store it securely immediately. It cannot be retrieved again.
Errors
| Status | Body | Cause |
|---|---|---|
| 401 | { "error": "unauthorized" } | Missing or invalid admin token |
| 422 | { "error": {...} } | Validation failed (Zod format) |
POST /attribute
Resolve attribution for a builder API key. Returns the builder's fee rule.
Auth: x-api-key: <builderApiKey> (the raw API key issued at registration)
Rate limit: 100 requests per 60 seconds per IP
This endpoint is called by the orchestrator internally when a builder apiKey is passed in POST /quote. You can also call it directly to verify your API key is active.
Request body
(empty — auth is via header only)
Response 200
{
"builderId": "bld_acme",
"builderBps": 30,
"maxBuilderBps": 50,
"payoutAddress": "0xPayoutAddress"
}| Field | Type | Description |
|---|---|---|
builderId | string | Builder ID |
builderBps | number | Current markup in basis points |
maxBuilderBps | number | Hard cap |
payoutAddress | string | Payout address |
Errors
| Status | Body | Cause |
|---|---|---|
| 404 | { "error": "unattributed" } | API key not found or invalid |
POST /accruals
Record a settled-sweep commission for a builder. Admin-gated. Called by the orchestrator watcher.
Auth: Authorization: Bearer <ADMIN_TOKEN>
The sweepId field is used as an idempotency key — the same accrual can be posted multiple times safely.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
builderId | string | Yes | The builder's ID |
sweepId | string | Yes | Unique sweep identifier (typically txHash_logIndex) |
token | string | Yes | Token contract address of the accrued amount |
amount | string (decimal) | Yes | Amount as a non-negative integer decimal string |
Response 201
{ "ok": true }Errors
| Status | Body | Cause |
|---|---|---|
| 401 | { "error": "unauthorized" } | Invalid admin token |
| 422 | { "error": {...} } | Validation failed |
GET /builders/:id/accruals
Get the accrual ledger for a builder. Admin-gated.
Auth: Authorization: Bearer <ADMIN_TOKEN>
Path param: id — builder ID (e.g. "bld_acme")
Response 200
{
"builderId": "bld_acme",
"balances": [
{ "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "3000000" },
{ "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "amount": "1500000" }
]
}| Field | Type | Description |
|---|---|---|
builderId | string | Builder ID |
balances | Array<{ token, amount }> | Per-token payable balances; amounts are decimal strings |
Errors
| Status | Body | Cause |
|---|---|---|
| 401 | { "error": "unauthorized" } | Invalid admin token |
Internal endpoints
The /admin/* console routes (list/register builders, PATCH markup bps within cap, view accruals, mark-paid, protocol-revenue + treasury views) and the machine POST /revenue ingest are internal: the admin routes are gated by Cloudflare Access SSO (served at admin.sweepster.xyz) and POST /revenue by the machine ADMIN_TOKEN. They are not part of the public builder integration surface and are not documented here.
API key security
- The
x-api-keyheader onPOST /attributeis the primary server-to-server integration auth — pass your API key as that header (never a JSON body field). - API keys are generated at registration and hashed before storage (SHA-256). Only the hash is stored.
- The raw key is returned once at registration and never again.
- Constant-time comparison is used for admin token validation to prevent timing attacks.
- Keep your API key in a server-side secret store (environment variable, Cloudflare secret, etc.). Never expose it client-side.