Seller setup
From an empty tenant to a live public offer — sign up, invite your team, mint an API key for your agent, publish a configuration version, and register a webhook endpoint.
Live. Every step on this page works today. The curl examples call this deployment's API at
https://app.skipthesell.comwith the OpenAPI document's path templates — replace{placeholders}with your resource ids. The same operations are in the checkout UI; the API is the product.
A seller is a tenant: an isolated account holding its catalog, offers, rules, deals and settings. This page walks the self-serve path from nothing to a listed, purchasable offer — the same path our own onboarding runs through. The vocabulary lives in core concepts.
Sign up#
Sign-up is one request, open to anyone. It creates the tenant and its first member — you, the owner — pending sign-in:
# operationId: create_tenant
curl -X POST https://app.skipthesell.com/api/v1/tenants \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <generate a fresh value, e.g. a UUID>' \
-d '{"name": "Acme", "owner_email": "[email protected]"}'
The Idempotency-Key is yours to generate — one fresh value per logical operation, a retry replays the stored response; this endpoint is open to anyone, so a guessable shared value can collide with another caller's. (Authenticated endpoints scope the key to your credential, where collisions are yours alone.)
The response carries the new tenant and your member. A single-use magic-link email goes to the owner address — one message carrying both the sign-in link and its sign-in code (the typed alternative, for links eaten by mail scanners or signing in on another device; either one signs you in once and cancels the other). Sign-in exchanges either half for a session:
# operationId: request_magic_link
curl -X POST https://app.skipthesell.com/api/v1/magic-links \
-H 'Content-Type: application/json' \
-d '{"email": "[email protected]"}'
# operationId: create_session
curl -c cookies.txt -X POST https://app.skipthesell.com/api/v1/sessions \
-H 'Content-Type: application/json' \
-d '{"token": "<token from the email>"}'
# operationId: exchange_sign_in_code — the same session from the typed code
# (case-insensitive; separators like the email's ABCDE-FG234 grouping are ignored)
curl -c cookies.txt -X POST https://app.skipthesell.com/api/v1/sign-in-codes \
-H 'Content-Type: application/json' \
-d '{"email": "[email protected]", "code": "ABCDE-FG234"}'
create_session sets the __Host-session cookie — save it with -c cookies.txt and send it back with -b cookies.txt on every call that needs the human's session. Check who you are:
# operationId: get_me
curl -b cookies.txt https://app.skipthesell.com/api/v1/me
To sign out (or sign another device out), revoke the session by its sess_… id, which GET /api/v1/sessions lists:
# operationId: delete_session
curl -b cookies.txt -X DELETE https://app.skipthesell.com/api/v1/sessions/{session_id}
Invite your team#
Members are humans with a role from the versioned permission schema (linked from meta.discovery as permission_schema). Schema v1's roles: owner, configurator, deal-manager, viewer. An invitation magic link is emailed; the member exists immediately:
# operationId: create_member
curl -b cookies.txt -X POST https://app.skipthesell.com/api/v1/members \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: invite-001' \
-d '{"email": "[email protected]", "role": "configurator"}'
An API key for your agent#
Your seller agent — the software you run to configure offers and read your deals — authenticates with a scoped API key, as Authorization: Bearer <key>:
# operationId: create_api_key
curl -b cookies.txt -X POST https://app.skipthesell.com/api/v1/api-keys \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: key-001' \
-d '{"name": "config agent", "scopes": ["draft:write", "draft:read", "draft:publish", "simulation:write", "configuration-version:read"]}'
The response carries the raw secret exactly once — store it now; it is never served again, and only its hash is stored. Scopes are the explicit list, frozen at creation and enforced ever after: pick them from the permission schema document, and give an agent the least set that does its job. Keys are listed (GET /api/v1/api-keys), revoked (POST /api/v1/api-keys/{api_key_id}/revoke) and optionally expiring (expires_at at creation). Every key event lands in the tenant's audit feed.
With that key, everything a human can do in the UI, your agent can do through the API — the agent quickstart shows the full seller-agent path.
Your seller handle and tenant settings#
Tenant settings are per-tenant operational policy outside the versioned configuration: the seller handle (your public identifier, addressed by your public offer listing), the deal-token TTL, the escalation email recipients and the start-flow rate limits:
# operationId: get_tenant_settings
curl -b cookies.txt https://app.skipthesell.com/api/v1/tenant-settings
Changes are a PATCH with the ETag from your read (If-Match), and never create a configuration version. The handle is renamable only while your tenant has no deals — once the first deal exists, your public URLs are permanent:
# operationId: update_tenant_settings
curl -b cookies.txt -X PATCH https://app.skipthesell.com/api/v1/tenant-settings \
-H 'Content-Type: application/json' \
-H 'If-Match: "<ETag from the read>"' \
-d '{"public_handle": "acme"}'
Publish a configuration#
Your catalog, offers and rules form a configuration document. Changes happen in a draft, are validated and simulated, and publish as an immutable configuration version — the thing every new deal pins. (The model is in core concepts; the pricing language is in the pricing rules primer.)
Create a draft, then put a configuration document in it. The document's exact shape is the published configuration v1 schema (linked from meta.discovery as configuration_schema); this minimal one offers one service in USD with a monthly flat fee and a per-seat price:
{
"version": 1,
"profile_version": 1,
"currencies": ["USD"],
"entity": {
"legal_name": "Acme, Inc.",
"address": "1 Main Street, Springfield, NY 10001, USA",
"signatory_title": "Authorized Signatory"
},
"prices": {
"base": {"kind": "flat_recurring", "amounts": {"USD": 120000}},
"seat": {"kind": "per_unit", "amounts": {"USD": 5000}}
},
"offers": {
"svc": {
"name": "Service",
"listed": true,
"terms": {"term_months": {"allowed": [12, 24]}},
"variables": {
"seats": {"type": "int", "min": 1, "max": 500, "default": 10}
},
"rules": [
{"id": "base", "phase": "lines", "effects": [{"line": {"name": "Base fee", "kind": "flat_monthly", "amount": "price(\"base\")"}}]},
{"id": "seats", "phase": "lines", "effects": [{"line": {"name": "Seats", "kind": "per_seat_monthly", "qty": "seats", "amount": "price(\"seat\")"}}]}
],
"done_condition": ["signed"]
}
}
}
Both amounts are per month: a flat_recurring or per_unit price carries a monthly amount — base is $1,200/mo, seat is $50 per seat per month — and the line kinds (flat_monthly, per_seat_monthly) carry that period; the buyer's billing frequency later scales one monthly amount to one invoice. The entity block names the legal party every published version's contracts bind and its countersignature records — legal_name, address and signatory_title are required for publishing, so even a minimal document carries it.
# operationId: create_draft
curl -X POST https://app.skipthesell.com/api/v1/drafts \
-H 'Authorization: Bearer <api key>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: draft-001' \
-d '{"name": "Launch offer"}'
# operationId: update_draft_document
curl -X PUT https://app.skipthesell.com/api/v1/drafts/{draft_id}/document \
-H 'Authorization: Bearer <api key>' \
-H 'Content-Type: application/json' \
-H 'If-Match: "<ETag from the create response>"' \
-d @configuration.json
The whole-document PUT is also the import path: what a published version exports can be put back verbatim, so you can keep your configuration in git. A draft may hold a document that does not validate yet — validate_draft is the answer to "would this publish?", running the full publish-time check (the entity block included) and reporting every diagnostic at once:
# operationId: validate_draft
curl -X POST https://app.skipthesell.com/api/v1/drafts/{draft_id}/validate \
-H 'Authorization: Bearer <api key>'
Before publishing, run a simulation: a hypothetical deal with an explicit evaluation context (currency, term, the buyer's choices), priced deterministically through the same engine production quotes use — with no side effects:
# operationId: simulate_draft
curl -X POST https://app.skipthesell.com/api/v1/drafts/{draft_id}/simulations \
-H 'Authorization: Bearer <api key>' \
-H 'Content-Type: application/json' \
-d '{"offer": "svc", "context": {"currency": "USD", "term_months": 12, "event": {"kind": "start", "at": "2026-11-07"}, "variables": {"seats": 25}}}'
Publishing compiles the draft into a new immutable version — publish re-validates, so what you validated is what ships, and a concurrent edit answers 412 instead:
# operationId: publish_draft
curl -X POST https://app.skipthesell.com/api/v1/drafts/{draft_id}/publish \
-H 'Authorization: Bearer <api key>' \
-H 'If-Match: "<ETag you validated against>"' \
-H 'Idempotency-Key: publish-001'
The response is the new cfgver_… version, identified by the content hash of its canonical document. Every deal started from now pins this version. And because the offer is marked listed, it is now public — no credential needed:
# operationId: list_public_offers
curl https://app.skipthesell.com/api/v1/sellers/{handle}/offers
From here, every deal a buyer starts lands in the seller console's deal pipeline — the same deals your agent reads through the API, where the UI is just one client:

Webhooks#
Register the endpoint your systems receive events on. The signing secret is generated server-side and shown exactly once, in this response:
# operationId: create_webhook_endpoint
curl -b cookies.txt -X POST https://app.skipthesell.com/api/v1/webhook-endpoints \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: hook-001' \
-d '{"url": "https://hooks.acme.example/skip", "event_types": ["*"]}'
Verify your receiver against the webhooks guide's signing scheme, then send yourself a test event and read the delivery log — every attempt recorded, replayable:
# operationId: test_webhook_endpoint
curl -b cookies.txt -X POST https://app.skipthesell.com/api/v1/webhook-endpoints/{webhook_endpoint_id}/test \
-H 'Idempotency-Key: test-001'
# operationId: list_webhook_deliveries
curl -b cookies.txt https://app.skipthesell.com/api/v1/webhook-endpoints/{webhook_endpoint_id}/deliveries
The audit feed#
Everything above — and everything your buyers do — is recorded in the tenant's audit feed: append-only, system-written, newest first. Read it with a principal holding the audit-event:read scope — your owner session, or an API key minted with the scope:
# operationId: list_audit_events
curl -b cookies.txt 'https://app.skipthesell.com/api/v1/audit-events?limit=50'
Where next#
- Pricing rules primer — how to price with rules, beyond flat fees.
- Agent quickstart — your agent's path from API key to a published offer, and a buyer agent's path through a deal.
- Webhooks — the signing scheme, the events, retries and replay.
- Self-hosting — the same product, your own binary.