Skip to content

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 / machinePOST /builders, POST /accruals, GET /builders/:id/accruals (admin Bearer token); the /admin/* console + POST /revenue routes 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

  1. GET /auth/nonce{ nonce }
  2. Build a SIWE message embedding that nonce (domain = the dashboard host) and have the wallet sign it.
  3. POST /builders/self with the message, signature, and a display name.

Request body

FieldTypeRequiredDescription
messagestringYesThe SIWE message the wallet signed (embeds the issued nonce)
signaturestringYesThe wallet's signature over message
namestring (1–64)YesDisplay name for the builder

Response 201

json
{
  "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 apiKey is shown only once. Save it immediately; it cannot be retrieved again (you can rotate it via POST /me/apikey/rotate).

Errors

StatusBodyCause
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
429Rate limitToo 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

FieldTypeRequiredDescription
messagestringYesThe signed SIWE message (embeds the nonce from GET /auth/nonce)
signaturestringYesThe wallet's signature over message

Response 200

json
{ "builder": { "id": "bld_acme", "name": "Acme Wallet", "builderBps": 0, "maxBuilderBps": 0, "payoutAddress": "0x..." } }

A Set-Cookie session header is returned alongside.

Errors

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

json
{ "id": "bld_acme", "name": "Acme Wallet", "builderBps": 0, "maxBuilderBps": 0, "payoutAddress": "0x...", "createdAtMs": 1717340400000 }

GET /me/accruals

Payable balances + lifetime totals, per token.

json
{
  "balances": [{ "token": "0xUSDC", "amount": "3000000" }],
  "lifetime": [{ "token": "0xUSDC", "amount": "9000000" }]
}

GET /me/activity

Per-sweep accrual ledger for the authenticated builder.

json
{
  "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

FieldTypeRequiredDescription
idstringYesUnique builder identifier (e.g. "bld_acme")
namestringYesHuman-readable builder name
builderBpsnumber (integer ≥ 0)YesBuilder markup in basis points (e.g. 30 = 0.30%)
maxBuilderBpsnumber (integer ≥ 0)YesHard cap on builder markup
payoutAddressstring (hex address)YesAddress for accrual payouts (0x...)

Response 201

json
{
  "builder": {
    "id": "bld_acme",
    "name": "Acme Wallet",
    "builderBps": 30,
    "maxBuilderBps": 50,
    "payoutAddress": "0xPayoutAddress",
    "createdAtMs": 1717340400000
  },
  "apiKey": "sk_live_..."
}

The apiKey is returned only once. Store it securely immediately. It cannot be retrieved again.

Errors

StatusBodyCause
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

json
{
  "builderId": "bld_acme",
  "builderBps": 30,
  "maxBuilderBps": 50,
  "payoutAddress": "0xPayoutAddress"
}
FieldTypeDescription
builderIdstringBuilder ID
builderBpsnumberCurrent markup in basis points
maxBuilderBpsnumberHard cap
payoutAddressstringPayout address

Errors

StatusBodyCause
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

FieldTypeRequiredDescription
builderIdstringYesThe builder's ID
sweepIdstringYesUnique sweep identifier (typically txHash_logIndex)
tokenstringYesToken contract address of the accrued amount
amountstring (decimal)YesAmount as a non-negative integer decimal string

Response 201

json
{ "ok": true }

Errors

StatusBodyCause
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

json
{
  "builderId": "bld_acme",
  "balances": [
    { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "3000000" },
    { "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "amount": "1500000" }
  ]
}
FieldTypeDescription
builderIdstringBuilder ID
balancesArray<{ token, amount }>Per-token payable balances; amounts are decimal strings

Errors

StatusBodyCause
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-key header on POST /attribute is 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.

Non-custodial · Atomic · Audit + bug bounty planned before mainnet value.