REST API v1
Sellf provides a full REST API for managing products, users, payments, refunds, coupons, webhooks, and more. Admin API endpoints are under /api/v1/.
Base URL: https://your-domain.com/api/v1
Features:
- 60+ endpoints covering all admin operations
- Fine-grained API keys with 13 permission scopes
- Zero-downtime key rotation with grace period
- Per-key rate limiting (1–1000 req/min)
- Cursor-based pagination
- OpenAPI 3.1 specification
Customer checkout, embedded checkout, Stripe webhook, waitlist signup, and subscription self-service routes live outside /api/v1 because they use browser sessions, public tokens, or Stripe signatures instead of API keys.
Quick Start
Section titled “Quick Start”1. Create an API key in Settings → API Keys.
2. Make your first request:
curl https://your-domain.com/api/v1/products \ -H "Authorization: Bearer sf_live_your_key_here"3. Response:
{ "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "My Product", "slug": "my-product", "price": 2900, "currency": "USD", "is_active": true } ], "pagination": { "cursor": null, "next_cursor": "eyJpZCI6Ii4uLiJ9", "has_more": true, "limit": 20 }}Authentication
Section titled “Authentication”Two methods are supported:
API Key (recommended for integrations)
Section titled “API Key (recommended for integrations)”Pass the key in the Authorization header:
curl https://your-domain.com/api/v1/products \ -H "Authorization: Bearer sf_live_a1b2c3d4..."Or use the X-API-Key header:
curl https://your-domain.com/api/v1/products \ -H "X-API-Key: sf_live_a1b2c3d4..."Session (admin panel)
Section titled “Session (admin panel)”The admin panel uses HTTP-only session cookies (Supabase Auth). Session auth grants full access to all endpoints.
Error responses
Section titled “Error responses”| HTTP Status | Error Code | Meaning |
|---|---|---|
| 401 | UNAUTHORIZED | No authentication provided |
| 401 | INVALID_TOKEN | Key is invalid, expired, or revoked |
| 403 | FORBIDDEN | Key lacks required scope |
| 429 | RATE_LIMITED | Too many requests |
API Keys
Section titled “API Keys”Key format
Section titled “Key format”| Prefix | Environment | Example |
|---|---|---|
sf_live_ | Production | sf_live_a1b2c3d4e5f6... (72 chars total) |
sf_test_ | Testing | sf_test_a1b2c3d4e5f6... (72 chars total) |
Security
Section titled “Security”- Keys are SHA-256 hashed before storage — plaintext is never saved
- The full key is returned only once at creation time
- Hash comparison uses timing-safe comparison to prevent timing attacks
- The
key_prefix(first 12 chars) is stored for display:sf_live_a1b2
Creating a key
Section titled “Creating a key”curl -X POST https://your-domain.com/api/v1/api-keys \ -H "Cookie: <session>" \ -H "Content-Type: application/json" \ -d '{ "name": "CI/CD Pipeline", "scopes": ["products:read", "products:write"], "rate_limit_per_minute": 120 }'Response (key shown only once):
{ "data": { "id": "...", "name": "CI/CD Pipeline", "key": "sf_live_a1b2c3d4e5f6...", "key_prefix": "sf_live_a1b2", "scopes": ["products:read", "products:write"], "rate_limit_per_minute": 120, "created_at": "2026-02-28T12:00:00Z" }}Note: API key management endpoints (
/api/v1/api-keys/*) require session auth — you cannot create keys using another key.
Scopes & Permissions
Section titled “Scopes & Permissions”Each API key has a scopes array that controls what it can access.
Available scopes
Section titled “Available scopes”| Scope | Description |
|---|---|
products:read | View products |
products:write | Create, update, delete products |
users:read | View users and access records |
users:write | Manage user access (grant, revoke, extend) |
coupons:read | View coupons |
coupons:write | Create, update, delete coupons |
analytics:read | View analytics, reports, and payment data |
webhooks:read | View webhook configurations |
webhooks:write | Manage webhooks (create, update, delete, test) |
integrations:write | Manage tracking and consent integrations |
refund-requests:read | View refund requests |
refund-requests:write | Process refund requests |
system:read | View system status and update info |
* | Full access to all resources |
Write implies read
Section titled “Write implies read”A key with products:write automatically has products:read permission. You don’t need to include both.
Presets
Section titled “Presets”Common scope combinations for quick setup:
| Preset | Scopes | Use case |
|---|---|---|
| Full Access | * | Admin integrations, MCP server |
| Read Only | All :read scopes | Dashboards, BI tools, reporting |
| Analytics | analytics:read | Revenue dashboards |
| Support | products:read, users:read, coupons:read | Customer support team |
Key Rotation
Section titled “Key Rotation”Rotate keys with zero downtime using a grace period:
curl -X POST https://your-domain.com/api/v1/api-keys/{id}/rotate \ -H "Cookie: <session>" \ -H "Content-Type: application/json" \ -d '{ "grace_period_hours": 24 }'During the grace period (0–168 hours, default 24):
- The new key is active immediately
- The old key continues to work until the grace period expires
- The new key inherits all scopes from the old key
This allows you to update your integration without any downtime.
Rate Limiting
Section titled “Rate Limiting”Each API key has a configurable rate limit (default: 60 requests/minute, range: 1–1000).
When exceeded, the API returns:
{ "error": { "code": "RATE_LIMITED", "message": "Rate limit exceeded. Maximum 60 requests per minute." }}HTTP status: 429 Too Many Requests
Response Format
Section titled “Response Format”Success
Section titled “Success”{ "data": { ... }}Success (paginated)
Section titled “Success (paginated)”{ "data": [ ... ], "pagination": { "cursor": "eyJpZCI6Ii4uLiJ9", "next_cursor": "eyJpZCI6Ii4uLiJ9", "has_more": true, "limit": 20 }}{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid input", "details": { "name": ["Name is required"], "price": ["Price must be positive"] } }}Error codes
Section titled “Error codes”| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | No authentication provided |
INVALID_TOKEN | 401 | Invalid, expired, or revoked key |
FORBIDDEN | 403 | Missing required scope |
VALIDATION_ERROR | 400 | Input validation failed |
INVALID_INPUT | 400 | Malformed request body |
NOT_FOUND | 404 | Resource not found |
CONFLICT | 409 | Resource conflict |
ALREADY_EXISTS | 409 | Duplicate resource |
RATE_LIMITED | 429 | Rate limit exceeded |
INTERNAL_ERROR | 500 | Server error |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable |
Pagination
Section titled “Pagination”All list endpoints use cursor-based pagination.
| Parameter | Default | Description |
|---|---|---|
limit | 20 | Items per page (max 100) |
cursor | — | Cursor from next_cursor of previous response |
search | — | Search query (where supported) |
sort_by | varies | Sort field |
sort_order | desc | asc or desc |
Example:
# First pagecurl ".../api/v1/products?limit=10"
# Next page (use next_cursor from previous response)curl ".../api/v1/products?limit=10&cursor=eyJpZCI6Ii4uLiJ9"Endpoints
Section titled “Endpoints”All endpoints are prefixed with /api/v1. Every endpoint also supports OPTIONS for CORS preflight.
Products
Section titled “Products”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /products | products:read | List products |
| POST | /products | products:write | Create product |
| GET | /products/{id} | products:read | Get product |
| PATCH | /products/{id} | products:write | Update product |
| DELETE | /products/{id} | products:write | Delete product |
| GET | /products/{id}/oto | products:read | Get one-time offer config |
| PUT | /products/{id}/oto | products:write | Set one-time offer |
| DELETE | /products/{id}/oto | products:write | Remove one-time offer |
Users & Access
Section titled “Users & Access”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /users | users:read | List users |
| GET | /users/{id} | users:read | Get user |
| GET | /users/{id}/access | users:read | List user’s product access |
| POST | /users/{id}/access | users:write | Grant product access |
| GET | /users/{id}/access/{accessId} | users:read | Get access record |
| PATCH | /users/{id}/access/{accessId} | users:write | Extend/modify access |
| DELETE | /users/{id}/access/{accessId} | users:write | Revoke access |
Coupons
Section titled “Coupons”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /coupons | coupons:read | List coupons |
| POST | /coupons | coupons:write | Create coupon |
| GET | /coupons/{id} | coupons:read | Get coupon |
| PATCH | /coupons/{id} | coupons:write | Update coupon |
| DELETE | /coupons/{id} | coupons:write | Delete coupon |
| GET | /coupons/{id}/stats | coupons:read | Get usage statistics |
Payments
Section titled “Payments”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /payments | analytics:read | List payments |
| GET | /payments/{id} | analytics:read | Get payment details |
| PATCH | /payments/{id} | analytics:read | Update payment metadata |
| GET | /payments/stats | analytics:read | Payment statistics |
| POST | /payments/export | analytics:read | Export payments (CSV) |
| POST | /payments/{id}/refund | refund-requests:write | Refund a payment, including partial refunds |
Order Bumps
Section titled “Order Bumps”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /order-bumps | products:read | List order bumps |
| POST | /order-bumps | products:write | Create order bump |
| GET | /order-bumps/{id} | products:read | Get order bump |
| PATCH | /order-bumps/{id} | products:write | Update order bump |
| DELETE | /order-bumps/{id} | products:write | Delete order bump |
Webhooks
Section titled “Webhooks”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /webhooks | webhooks:read | List webhooks |
| POST | /webhooks | webhooks:write | Create webhook |
| GET | /webhooks/{id} | webhooks:read | Get webhook |
| PATCH | /webhooks/{id} | webhooks:write | Update webhook |
| DELETE | /webhooks/{id} | webhooks:write | Delete webhook |
| POST | /webhooks/{id}/test | webhooks:write | Send test event |
| GET | /webhooks/logs | webhooks:read | List delivery logs |
| POST | /webhooks/logs/{logId}/retry | webhooks:write | Retry failed delivery |
| POST | /webhooks/logs/{logId}/archive | webhooks:write | Archive log entry |
Integrations
Section titled “Integrations”| Method | Path | Scope | Description |
|---|---|---|---|
| PATCH | /integrations | integrations:write | Update tracking and consent integrations |
Supported fields: gtm_container_id, gtm_server_container_url, gtm_ss_enabled, google_ads_conversion_id, google_ads_conversion_label, facebook_pixel_id, facebook_capi_token, facebook_test_event_code, fb_capi_enabled, send_conversions_without_consent, umami_website_id, umami_script_url, cookie_consent_enabled, consent_logging_enabled.
Supported outgoing event types include purchases, leads, waitlist signups, refund issued events, subscription lifecycle events, and invoice payment events.
Analytics
Section titled “Analytics”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /analytics/dashboard | analytics:read | Dashboard overview |
| GET | /analytics/revenue | analytics:read | Revenue breakdown |
| GET | /analytics/top-products | analytics:read | Top performing products |
Refund Requests
Section titled “Refund Requests”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /refund-requests | refund-requests:read | List refund requests |
| GET | /refund-requests/{id} | refund-requests:read | Get request details |
| PATCH | /refund-requests/{id} | refund-requests:write | Process (approve/deny) |
Variant Groups
Section titled “Variant Groups”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /variant-groups | products:read | List variant groups |
| POST | /variant-groups | products:write | Create variant group |
| GET | /variant-groups/{id} | products:read | Get variant group |
| PATCH | /variant-groups/{id} | products:write | Update variant group |
| DELETE | /variant-groups/{id} | products:write | Delete variant group |
System
Section titled “System”| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /system/status | system:read | System status and version |
| GET | /system/update-check | system:read | Check for updates |
| POST | /system/upgrade | system:read | Trigger self-upgrade |
| GET | /system/upgrade-status | system:read | Poll upgrade progress |
API Keys (session auth only)
Section titled “API Keys (session auth only)”These endpoints require admin session authentication — API keys cannot manage other keys.
| Method | Path | Description |
|---|---|---|
| GET | /api-keys | List your API keys |
| POST | /api-keys | Create new key (returns secret once) |
| GET | /api-keys/{id} | Get key details |
| PATCH | /api-keys/{id} | Update key (name, scopes, rate limit) |
| DELETE | /api-keys/{id} | Revoke key |
| POST | /api-keys/{id}/rotate | Rotate with grace period |
Public, Session, And Stripe-Signed Routes
Section titled “Public, Session, And Stripe-Signed Routes”These routes are intentionally not part of API-key authenticated /api/v1.
Embedded Checkout
Section titled “Embedded Checkout”| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/embed/checkout-session | Public origin validation | Create an embedded checkout session for an allowed origin |
| POST | /api/embed/free-access | Public origin validation | Grant access for free embedded products |
| GET | /embed/v1/checkout.js | Public script | Self-hosted checkout embed script for external pages |
Customer Subscriptions
Section titled “Customer Subscriptions”| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/subscriptions | Session | List the current customer’s subscriptions |
| POST | /api/subscriptions/{id}/cancel | Session | Cancel at period end |
| POST | /api/subscriptions/{id}/resume | Session | Resume before the current period ends |
Stripe Webhook
Section titled “Stripe Webhook”| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/webhooks/stripe | Stripe signature | Process checkout, refund, dispute, subscription, upcoming renewal, and invoice events |
Subscribe the Stripe endpoint to the events listed by STRIPE_WEBHOOK_EVENTS in admin-panel/src/lib/constants.ts.
Waitlist
Section titled “Waitlist”| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/waitlist/signup | Public or session | Capture waitlist signups and dispatch waitlist.signup outgoing webhooks |
OpenAPI Specification
Section titled “OpenAPI Specification”The OpenAPI 3.1 registry and generator live in admin-panel/src/lib/api/schemas/openapi.ts.
Use it as the source for generated API docs or Bruno/Postman imports when exposing a spec endpoint in your deployment.