API Reference
Drop Backend API Reference
Project:Auto-generated{{PROJECT_NAME}}fromVersion:source{{VERSION}}codeDate:analysis.{{DATE}}AllAuthor:file{{AUTHOR}}referencesStatus:areDraftrelative|toIn Review | ApprovedReviewers:{{REVIEWERS}}src/drop-app/src/.
DocumentOverview
Drop uses Next.js App Router API routes (app/api/). All responses use a consistent JSON envelope:
{ "data": { ... } } // Success
{ "error": "code", "message": "...", "details": [...] } // Error
Authentication is via httpOnly cookie (drop_token) containing a signed JWT (HS256, 24h expiry).
Pass-Through Model: Drop uses a PSD2 pass-through model — it NEVER holds customer money. There is no wallet, no balance, no top-up. User funds remain in their bank account at all times. Drop uses:
- AISP (Account Information Service Provider) — reads bank balance via Open Banking
- PISP (Payment Initiation Service Provider) — initiates transfers directly from user's bank account
The bank_accounts.balance field stores the last AISP-read balance from the user's real bank (cached for display) — NOT a Drop-held balance.
Authentication
POST /api/auth/register
Create a new user account.
app/api/auth/register/route.ts |
|||
| Auth | |||
| Rate Limit |
1. API Overview & Conventions
APIRequest style: {{RESTful HTTP/JSON | GraphQL | gRPC}}
API version strategy: {{URL versioning: /v1/ | Header versioning}}
OpenAPI spec: {{https://api.domain.com/docs/openapi.json}}
Interactive docs: {{https://api.domain.com/docs}}
Design conventions:Body:
Resources named as plural nouns:/users,/orders,/productsHTTP methods map to CRUD: GET (read), POST (create), PUT (replace), PATCH (update), DELETE (remove)Response format: always JSONTimestamps: ISO 8601 UTC (2024-01-15T10:30:00Z)IDs: UUID v4 stringsBooleans:true/false(never1/0)Empty collections:[](nevernull)Missing optional fields: omitted (nevernullunless semantically null)
2. Base URLs
| Required | Validation | ||
|---|---|---|---|
string |
Yes | RFC-like regex, unique | |
string |
Yes | Min 8 chars, must contain letters + digits | |
| string | Yes | — 1-100 chars, at least one letter, no HTML/script |
|
| lastName | string | Yes | Same as firstName |
| phone | string | No | International format +XXXXXXXXXXXX (8-15 digits) |
| dateOfBirth | string | Yes | ISO date string, must be >= 18 years old |
3. Authentication
Method: Bearer Token (JWT)
Obtain tokens: POST /auth/login (see Auth section below)
Include in requests:
Authorization: Bearer <access_token>
Token lifetimes:
Access token: 15 minutesRefresh token: 30 days (rotate on use)
Refresh tokens: POST /auth/refresh with { "refreshToken": "..." } in body.
API Key authentication (machine-to-machine):
X-API-Key: <api_key>
API keys are scoped and managed at {{https://dashboard.domain.com/api-keys}}.
4. Common Request/Response Headers
4.1 Request Headers
| | |
| | |
| | |
| ||
| |
4.2Success Response Headers
| |
| |
| |
| |
| |
|
5. Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format",
"code": "INVALID_FORMAT"
}
],
"requestId": "req_7f3a2b1c",
"timestamp": "2024-01-15T10:30:00Z"
}
}
Standard error codes:
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
| ||
|
6. Resources
6.1 Authentication
POST /auth/login
Authenticate user and receive token pair.
Auth required: No
Request body:
{
"email": "[email protected]",
"password": "{{password}}"
}
Response 201):200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g...",
"expiresIn": 900,
"user"data": {
"id": "usr_01HX7.usr_...",
"email": "[email protected]",
"name"firstName": "John Doe"...",
"role"lastName": "user"...",
"dateOfBirth": "...",
"kycStatus": "pending",
"createdAt": "2026-..."
}
}
Error responses:Responses:
| Status | Code | Condition |
|---|---|---|
| bad_request | Invalid JSON body | |
| 409 | conflict | Email already registered |
| 422 | validation_error | Field validation failures (returned in details array) |
| 429 | rate_limited | Too many requests |
POST /api/auth/login
Authenticate with email and password.
| Field | Source |
|---|---|
| File | |
| None | |
| Rate Limit | 10 req/min per IP |
Request Body:
| Field | Type | Required |
|---|---|---|
| string | Yes | |
| password | string | Yes |
Success Response (200):
{
"data": {
"id": "usr_...",
"email": "...",
"firstName": "...",
"lastName": "...",
"kycStatus": "approved"
}
}
Error Responses:
| Status | Code | Condition |
|---|---|---|
| 400 | bad_request | Missing email or password |
| 401 | unauthorized | Invalid credentials |
| 429 | rate_limited |
POSTGET /api/auth/refresh
Rotate access and refresh tokens.
Auth required: No
Request body:
{ "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g..." }
Response 200 OK: Same as login response.
POST /auth/logout
Revoke refresh token.
Auth required: Yes (Bearer)
Request body:
{ "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g..." }
Response 204 No Content
6.2 Usersme
Endpoints:Get current authenticated user with bank accounts.
File |
| ||
Auth |
| ||
| | ||
| | ||
| | ||
| | ||
| |
Success Response (200):
{
"data": {
"id": "usr_...",
"email": "...",
"firstName": "...",
"lastName": "...",
"totalBalance": 58030.0,
"bankAccounts": [
{
"id": "ba_1",
"bankName": "DNB",
"accountNumber": "1234.56.78901",
"balance": 45230.0,
"currency": "NOK",
"isPrimary": true
}
],
"kycStatus": "approved",
"createdAt": "..."
}
}
POST /api/auth/logout
Logout and revoke all sessions.
| Field | Source |
|---|---|
| File | app/api/auth/logout/route.ts |
| Auth | Required (cookie) |
Calls revokeAllSessions() to invalidate all session records, then clears the auth cookie.
Success Response (200):
{ "message": "Logged out" }
POST /api/auth/refresh
Refresh the authentication token (issue new JWT, create new session record).
| Field | Source |
|---|---|
| File | app/api/auth/refresh/route.ts |
| Auth | Required (cookie) |
Success Response (200):
{
"data": {
"userId": "usr_...",
"email": "...",
"role": "user"
}
}
Transactions
GET /usersapi/transactions
List usersuser's transactions with pagination and filtering.
Query Success Response Note:
Field
Source
File
app/api/transactions/route.ts
Auth
required:Required
Adminparameters:Parameters:
ParameterParamType
Default
DescriptionNotes
pageintegerint1PageMin number1
limitpageSizeintegerint2025ItemsMin per1, pageMax (max: 100)50
typesearchstring
—-Search nameremittance or email (min 2 chars)
role
string
—
Filter by role: admin, user, viewerqr_payment
statusstring
-activeFilter by status: , activeprocessing, inactivecompletedall
sort
string
createdAt
Sort field
dir
string
desc
Sort direction:or asc, descfailed(200):200 OK{
"data": [
{
"id": "usr_01HX7..."tx_rem_1",
"email"type": "[email protected]",
"name": "Jane Doe",
"role": "user"remittance",
"status": "active"completed",
"amount": -2000,
"currency": "NOK",
"recipientName": "Mama Jasmina",
"createdAt": "2024-01-15T10:30:00Z",
..."updatedAt": "2024-01-15T10:30:00Z"
}
],
"pagination": { "page": 1, "pageSize"limit": 25,20, "total": 142,
"totalPages": 63 }
}
amount is negated in the response (always shown as outgoing).
api/transactions/[id]
GET /
users/:idAuthGet required:Selftransaction ordetails Admin
Pathexchange parameters:rate info.
| File | |
|
Success Response (200):200 OK
{
"data": {
"id": "usr_01HX7.tx_rem_1",
"type": "remittance",
"status": "completed",
"sendAmount": 2000,
"sendCurrency": "NOK",
"receiveAmount": 23400,
"receiveCurrency": "RSD",
"exchangeRate": 11.7,
"fee": 10,
"total": 2010,
"recipientName": "Mama Jasmina",
"recipientCountry": "Serbia",
"createdAt": "...",
"completedAt": "..."
}
}
GET /api/transactions/summary
Get transaction summary statistics (all-time and this month).
| Field | Source |
|---|---|
| File | app/api/transactions/summary/route.ts |
| Auth | Required |
Success Response (200):
{
"data": {
"allTime": {
"totalCount": 3,
"totalSent": 5000,
"totalPaid": 129,
"remittanceCount": 2,
"qrPaymentCount": 1
},
"thisMonth": { "..." }
}
}
POST /api/transactions/remittance
Create a remittance (international money transfer).
| Field | Source |
|---|---|
| File | app/api/transactions/remittance/route.ts |
| Auth | Required |
| Rate Limit | 10 req/min per IP |
| KYC | Must be approved |
Request Body:
| Field | Type | Required | Validation |
|---|---|---|---|
| recipientId | string | Yes | Must belong to user |
| amount | number | Yes | 100-50,000 NOK, max 2 decimal places |
| currency | string | No | Defaults to NOK |
| bankAccountId | string | No | Defaults to primary bank account |
Business Logic:
- Verify recipient belongs to user
- Look up exchange rate for recipient's currency
- Verify bank account exists and has sufficient balance
- Fee: 0.5% of amount
- Debit bank account (atomic transaction)
- Create transaction record with status
processing
Success Response (201):
{
"data": {
"id": "tx_rem_...",
"type": "remittance",
"status": "processing",
"sendAmount": 2000,
"sendCurrency": "NOK",
"receiveAmount": 23400,
"receiveCurrency": "RSD",
"exchangeRate": 11.7,
"fee": 10,
"feePercent": 0.5,
"total": 2010,
"recipientName": "...",
"recipientCountry": "Serbia",
"fromAccount": "DNB",
"eta": "1-2 business days",
"createdAt": "..."
}
}
Error Responses:
| Status | Code | Condition |
|---|---|---|
| 400 | bad_request | Missing/invalid fields |
| 400 | no_bank_account | No linked bank account |
| 402 | insufficient_balance | Bank account balance too low |
| 403 | kyc_required | KYC not approved |
| 404 | not_found | Recipient not found |
| 422 | validation_error | Unsupported currency corridor |
POST /api/transactions/qr-payment
Create a QR payment to a merchant.
| Field | Source |
|---|---|
| File | app/api/transactions/qr-payment/route.ts |
| Auth | Required |
| Rate Limit | 10 req/min per IP |
Request Body:
| Field | Type | Required | Validation |
|---|---|---|---|
| merchantId | string | Yes | Must exist |
| amount | number | Yes | 1-100,000 NOK, max 2 decimal places |
Business Logic:
- Verify merchant exists
- Get user's primary bank account
- Fee: 1% of amount
- Debit bank account (atomic transaction)
- Create transaction with status
completed(instant)
Success Response (201):
{
"data": {
"id": "tx_qr_...",
"type": "qr_payment",
"status": "completed",
"amount": 129,
"currency": "NOK",
"fee": 1.29,
"feePercent": 1,
"merchantName": "Ahmetov Kebab",
"merchantId": "mer_1",
"fromAccount": "DNB",
"createdAt": "..."
}
}
Recipients
GET /api/recipients
List user's recipients with pagination.
| Field | Source |
|---|---|
| File | app/api/recipients/route.ts |
| Auth | Required |
Query Parameters: page (default 1), limit (default 20, max 50)
Bank account numbers are masked in response (e.g., *****5678).
Supported Countries: RS (Serbia), BA (Bosnia), PL (Poland), PK (Pakistan), TR (Turkey)
POST /api/recipients
Add a new recipient.
| Field | Source |
|---|---|
| File | app/api/recipients/route.ts |
| Auth | Required |
Request Body:
| Field | Type | Required | Validation |
|---|---|---|---|
| name | string | Yes | validateName() |
| country | string | Yes | Must be in supported list |
| currency | string | Yes | - |
| bankAccount | string | Yes | - |
| bankName | string | No | Sanitized to 200 chars |
DELETE /api/recipients/[id]
Delete a recipient.
| Field | Source |
|---|---|
| File | app/api/recipients/[id]/route.ts |
| Auth | Required |
Returns 204 No Content on success. Returns 404 if recipient not found or not owned by user.
Cards (FUTURE — feature-flagged, all flags default to false)
Note: The entire Cards section is a FUTURE feature, gated behind feature flags. All card-related feature flags default to
false. These endpoints exist in code but return 404 when flags are disabled. Cards require a card issuing partner (e.g., Stripe Issuing) before activation.
GET /api/cards
List user's cards (excludes cancelled).
| Field | Source |
|---|---|
| File | app/api/cards/route.ts |
| Auth | Required |
POST /api/cards
Create a new card (virtual or physical).
| Field | Source |
|---|---|
| File | app/api/cards/route.ts |
| Auth | Required |
Request Body:
| Field | Type | Required | Notes |
|---|---|---|---|
| type | string | No | virtual (default) or physical |
GET /api/cards/[id]
Get card details. Card number is masked (---- ---- ---- XXXX), CVV is hidden (---).
| Field | Source |
|---|---|
| File | app/api/cards/[id]/route.ts |
| Auth | Required |
PCI-DSS compliant: never exposes full card number or CVV.
PATCH /api/cards/[id]
Freeze or unfreeze a card.
| Field | Source |
|---|---|
| File | app/api/cards/[id]/route.ts |
| Auth | Required |
Request Body: { "status": "active" | "frozen" }
DELETE /api/cards/[id]
Cancel a card (soft delete — sets status to cancelled).
| Field | Source |
|---|---|
| File | app/api/cards/[id]/route.ts |
| Auth | Required |
POST /api/cards/[id]/physical
Order physical version of a virtual card.
| Field | Source |
|---|---|
| File | app/api/cards/[id]/physical/route.ts |
| Auth | Required |
| Feature Flag | physicalCards (returns 404 if disabled) |
Request Body: { "address": "..." } (min 10 chars)
POST /api/cards/[id]/pin
Set PIN for a card.
| Field | Source |
|---|---|
| File | app/api/cards/[id]/pin/route.ts |
| Auth | Required |
| Feature Flag | cardPin (returns 404 if disabled) |
Request Body: { "pin": "1234" } (exactly 4 digits)
PIN is hashed with bcrypt before storage.
GET /api/cards/[id]/limits
Get spending limits for a card.
| Field | Source |
|---|---|
| File | app/api/cards/[id]/limits/route.ts |
| Auth | Required |
| Feature Flag | spendingLimits (returns 404 if disabled) |
PUT /api/cards/[id]/limits
Set a spending limit for a card.
| Field | Source |
|---|---|
| File | app/api/cards/[id]/limits/route.ts |
| Auth | Required |
| Feature Flag | spendingLimits (returns 404 if disabled) |
Request Body:
| Field | Type | Required | Validation |
|---|---|---|---|
| limitType | string | Yes | daily, weekly, monthly, or transaction |
| amount | number | Yes | Must be positive |
Replaces any existing limit of the same type.
Exchange Rates
GET /api/rates
Get all exchange rates from NOK.
| Field | Source |
|---|---|
| File | app/api/rates/route.ts |
| Auth | None |
| Rate Limit | 120 req/min per IP |
Success Response (200):
{
"data": {
"baseCurrency": "NOK",
"rates": { "RSD": 11.7, "BAM": 1.04, "PLN": 0.41, "PKR": 26.8, "TRY": 3.45, "EUR": 0.089 },
"updatedAt": "..."
}
}
GET /api/rates/[currency]
Get rate for a specific currency pair.
| Field | Source |
|---|---|
| File | app/api/rates/[currency]/route.ts |
| Auth | None |
| Rate Limit | 120 req/min per IP |
Response includes fee: 0.005 (0.5% remittance fee).
Notifications
GET /api/notifications
List all notifications for user.
| Field | Source |
|---|---|
| File | app/api/notifications/route.ts |
| Auth | Required |
| Feature Flag | notifications (default: enabled) |
PATCH /api/notifications
Mark notifications as read.
| Field | Source |
|---|---|
| File | app/api/notifications/route.ts |
| Auth | Required |
| Feature Flag | notifications |
Request Body: { "notificationIds": ["noti_..."] }
- Max 100 IDs per request
- IDs validated against format
^[a-z]+_[a-f0-9]{16}$
Settings
GET /api/settings
Get user settings (creates defaults if none exist).
| Field | Source |
|---|---|
| File | app/api/settings/route.ts |
| Auth | Required |
Defaults: currency=NOK, language=nb, pushEnabled=true, emailEnabled=true
PATCH /api/settings
Update user settings.
| Field | Source |
|---|---|
| File | app/api/settings/route.ts |
| Auth | Required |
Request Body (all optional):
| Field | Type | Validation |
|---|---|---|
| currency | string | Whitelist: EUR, USD, GBP, BAM, CHF, PLN, NOK, RSD, TRY, PKR |
| language | string | Whitelist: nb, en, bs, sq |
| pushEnabled | boolean | - |
| emailEnabled | boolean | - |
Merchants
POST /api/merchants/register
Register as a merchant.
| Field | Source |
|---|---|
| File | app/api/merchants/register/route.ts |
| Auth | Required |
Request Body:
| Field | Type | Required | Validation |
|---|---|---|---|
| businessName | string | Yes | validateName() |
| orgNumber | string | Yes | Exactly 9 digits, unique |
| address | string | No | Sanitized to 300 chars |
| bankAccount | string | Yes | Payout account |
Upgrades user role to merchant. Returns a QR code URI (drop://pay/{merchantId}).
GET /api/merchants/dashboard
Get merchant dashboard stats.
| Field | Source |
|---|---|
| File | app/api/merchants/dashboard/route.ts |
| Auth | Required (merchant role) |
Query Parameters: period — today (default), week, month
Returns: revenue, transactionCount, fees, netRevenue, nextPayout, payoutTime.
GET /api/merchants/qr
Get merchant QR code data.
| Field | Source |
|---|---|
| File | app/api/merchants/qr/route.ts |
| Auth | Required (merchant role) |
Returns: merchantId, businessName, qrValue (drop://pay/{id}), address.
GET /api/merchants/transactions
List merchant's QR payment transactions with pagination.
| Field | Source |
|---|---|
| File | app/api/merchants/transactions/route.ts |
| Auth | Required (merchant role) |
Query Parameters: page, limit
Customer names are partially anonymized (first name + last initial).
GDPR & Compliance
GET /api/user/data-export
Export all user data (GDPR right to data portability).
| Field | Source |
|---|---|
| File | app/api/user/data-export/route.ts |
| Auth | Required |
Creates a data_access_request record with type export and status completed.
Success Response (200):
{
"data": {
"user": {
"id": "usr_...",
"email": "[email protected]"...",
"name"first_name": "Jane...",
Doe""last_name": "...",
"phone": "+47...",
"date_of_birth": "1995-03-15",
"kyc_status": "approved",
"role": "user",
"status"created_at": "active"..."
},
"profile"transactions": [ {...}, {...} ],
"recipients": [ {...}, {...} ],
"bankAccounts": [ {...} ],
"settings": { "avatarUrl"currency": "https:NOK", "language": "nb", ... },
"consents": [ {...}, {...} ]
},
"exportedAt": "2026-02-17T..."
}
DELETE /api/user/account
Request account deletion (GDPR right to erasure).
| Field | Source |
|---|---|
| File | app/api/user/account/route.ts |
| Auth | Required |
Behavior:
- Soft-deletes user (sets
deleted_attimestamp) - Revokes all active sessions
- Creates
data_access_requestwith typeerasureand statuscompleted - Important: Data retained for 5 years per AML/KYC legal requirements (hvitvaskingsloven)
Success Response (200):
{
"message": "Account scheduled for deletion",
"retentionNote": "Data retained for 5 years per AML requirements"
}
GET /cdn.domain.com/avatars/.api/consents
List user's GDPR consents.
| Field | Source |
|---|---|
| File | app/api/consents/route.ts |
| Auth | Required |
Success Response (200):
{
"data": [
{
"id": "con_...",
"bio"user_id": "Software developer"
}usr_...",
"createdAt"consent_type": "2024-01-15T10:30:00Z"terms",
"updatedAt"granted": 1,
"granted_at": "2024-01-15T10:30:00Z"2026-02-17T...",
"withdrawn_at": null,
"ip_address": "192.0.2.1"
}
]
}
POST /api/consents
Grant or withdraw a consent.
| Field | Source |
|---|---|
| File | app/api/consents/route.ts |
| Auth | Required |
Request Body:
| Field | Type | Required | Validation |
|---|---|---|---|
| consentType | string | Yes | Must be one of: terms, privacy, marketing, cookies_analytics, cookies_marketing |
| granted | boolean | Yes | true to grant, false to withdraw |
Behavior:
- If consent exists: updates
grantedfield and sets eithergranted_atorwithdrawn_at - If consent doesn't exist: creates new consent record
- Records user's IP address with consent action
Success Response (200 for update, 201 for new):
{
"data": {
"id": "con_...",
"consent_type": "marketing",
"granted": 1,
"granted_at": "2026-02-17T...",
"withdrawn_at": null,
"ip_address": "192.0.2.1"
}
}
Error responses:Responses:
| Status | Code | Condition |
|---|---|---|
| 400 | bad_request | Invalid consent type or missing fields |
GET /api/complaints
List user's complaints.
| Field | Source |
|---|---|
| File | app/api/complaints/route.ts |
| Auth | Required |
Query Parameters:
| Param | Type | Default | Notes |
|---|---|---|---|
| page | int | 1 | Pagination page number |
| limit | int | 10 | Items per page, max 100 |
Success Response (200):
{
"data": [
{
"id": "cmp_...",
"category": "transaction",
"subject": "Transaction delayed",
"description": "My remittance to Serbia is delayed...",
"status": "received",
"resolution": null,
"created_at": "2026-02-17T...",
"resolved_at": null
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 3,
"totalPages": 1
}
}
POST /api/complaints
Submit a complaint (Finansavtaleloven §3-53 compliance).
| Field | Source |
|---|---|
| File | app/api/complaints/route.ts |
| Auth | Required |
Request Body:
| Field | Type | Required | Validation |
|---|---|---|---|
| category | string | Yes | Must be one of: transaction, service, fees, privacy, technical, other |
| subject | string | Yes | Max 200 chars, sanitized |
| description | string | Yes | Max 2000 chars, sanitized |
Success Response (201):
{
"data": {
"id": "cmp_...",
"category": "fees",
"subject": "High transfer fee",
"description": "...",
"status": "received",
"created_at": "2026-02-17T..."
},
"commitmentNote": "We will review and respond to your complaint within 15 business days per Finansavtaleloven §3-53"
}
Error Responses:
| Status | Code | Condition |
|---|---|---|
| 400 | bad_request | Invalid category or empty fields |
POST /api/transactions/disclosure
Get full transaction fee and exchange rate disclosure before initiating payment.
| Field | Source |
|---|---|
| File | app/api/transactions/disclosure/route.ts |
| Auth | Required |
Request Body:
| Field | Type | Required | Notes |
|---|---|---|---|
| type | string | Yes | remittance or qr_payment |
| amount | number | Yes | Must be positive |
| currency | string | No | Defaults to NOK |
| recipientId | string | Conditional | Required for remittance |
Success Response (200):
{
"amount": 2000,
"fee": 10,
"feePercentage": 0.5,
"exchangeRate": 10.17,
"receiveAmount": 20340,
"receiveCurrency": "RSD",
"estimatedDelivery": "1-2 business days",
"totalCost": 2010
}
Fee Calculation:
- Remittance: 0.5% of amount
- QR payment: 1.0% of amount
Delivery Time:
- QR payment: "Instant"
- Remittance (EEA): "1-2 business days"
- Remittance (non-EEA): "2-4 business days"
GET /api/transactions/[id]/receipt
Get transaction receipt with full details.
| Field | Source |
|---|---|
| File | app/api/transactions/[id]/receipt/route.ts |
| Auth | Required |
Success Response (200):
{
"data": {
"transactionId": "tx_rem_1",
"date": "2026-02-17T...",
"type": "remittance",
"amount": 2000,
"currency": "NOK",
"fee": 10,
"exchangeRate": 10.17,
"receiveAmount": 20340,
"receiveCurrency": "RSD",
"recipient": {
"name": "Mama Jasmina",
"country": "RS"
},
"reference": "tx_rem_1",
"status": "completed",
"estimatedCompletion": null,
"completedAt": "2026-02-17T..."
}
}
Error Responses:
| Status | Code | Condition |
|---|---|---|
| 404 | not_found |
|
|
POSTHealth /users
Check
Auth required: Admin
Request body:
{
"email": "[email protected]",
"name": "New User",
"role": "user",
"password": "{{password}}"
}
Response 201 Created: Full user object (same as
GET /users/:id)
PATCH /users/:id
api/health
AuthSystem required:health Self or Admin
Request bodycheck (allno fieldsauth optional):
{
"name": "Updated Name",
"profile": {
"bio": "Updated bio"
}
}
Response 200 OK: Updated user object.
DELETE /users/:id
Soft-deletes user (sets deletedAt, anonymizes PII)required).
Auth required: Admin
Response 204 No Content
6.3 {{RESOURCE_NAME}}
Endpoints:
File |
| | |
Auth |
| | |
| | | |
| | | |
| | None |
TODO:Success Document all endpoints for this resource following the pattern above.
7. Pagination Format
All list endpoints return the same pagination envelope:
{
"data": [...],
"pagination": {
"page": 1,
"pageSize": 25,
"total": 142,
"totalPages": 6,
"hasNextPage": true,
"hasPreviousPage": false
}
}
Cursor paginationResponse (high-performance, for infinite scroll):
GET /feed?cursor=eyJpZCI6MTIzfQ&pageSize=20
Response includes nextCursor — pass as cursor in next request.
8. Filtering & Sorting Conventions
Filter parameters:
GET /orders?status=pending&createdAt[gte]=2024-01-01&total[lte]=1000
| ||
| | |
| | |
| | |
| | |
| | |
| |
Sort: ?sort=createdAt&dir=desc (default: createdAt desc)
9. Webhooks Documentation
Webhook endpoint: Configured per-account at {{https://dashboard.domain.com/webhooks}}
Delivery: HTTP POST with JSON body, signed with HMAC-SHA256.
Signature verification:
const signature = req.headers['x-webhook-signature'];
const computed = crypto
.createHmac('sha256', webhookSecret)
.update(rawBody)
.digest('hex');
const valid = crypto.timingSafeEqual(
Buffer.from(signature), Buffer.from(computed)
);
Event envelope:
{
"id": "evt_01HX7...",
"type": "user.created",
"data": { /* resource object */ },
"timestamp": "2024-01-15T10:30:00Z",
"version": "1"
}
Available events:
| |
| |
| |
| |
Retry policy: 5 retries with exponential backoff. Undeliverable after 24 hours → marked as failed.
10. Rate Limiting
Response when rate limited (429 Too Many Requests)200):
{
"error": {
"code"status": "RATE_LIMITED"ok",
"message"version": "Too many requests. Please retry after 60 seconds."0.1.0",
"retryAfter"uptime": 603600,
}"db": "connected",
"dbLatencyMs": 1,
"timestamp": "..."
}
Returns
11.503 Codewith Examples
cURL
# Login
curl -X POST https://api.domain.com/v1/auth/login \
-Hstatus: "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "secret"}'
# Get users (with token)
curl -X GET "https://api.domain.com/v1/users?page=1&pageSize=10" \
-H "Authorization: Bearer <access_token>"
error"JavaScript (fetch)
const response = await fetch('https://api.domain.com/v1/users', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
}); if (!response.ok)database {is constunreachable.
error = await response.json();
throw new Error(error.error.message);
}
const { data, pagination } = await response.json();
Python
import httpx
client = httpx.Client(
base_url="https://api.domain.com/v1",
headers={"Authorization": f"Bearer {access_token}"}
)
response = client.get("/users", params={"page": 1, "pageSize": 10})
response.raise_for_status()
result = response.json()
12. SDK Availability
| | | |
| | | |
| | |