Authentication
SwitchLock uses two authentication mechanisms depending on the caller type, plus two context headers that scope requests to the right company and prevent duplicate operations.
Headers at a glance
x-api-keyYour server-to-server API key. Identifies and authenticates your backend application. Use this for all programmatic integrations.
Authorization: Bearer <token>A short-lived JWT issued at login. Used by the dashboard and admin interfaces. Not recommended for automated server integrations.
x-company-idScopes the request to a specific tenant. Required on almost every authenticated endpoint. Your API key is pre-associated with a company, but this header must still be sent.
x-idempotency-keyA client-generated UUID for financial mutations (deposits, withdrawals, payment link generation). Prevents duplicate operations on retries. Required on treasury endpoints.
API Key Authentication
API keys are the primary authentication method for server-to-server integrations. They are generated per company, hashed with SHA-256 before storage, and never retrievable after initial creation.
curl -X GET "https://api.switchlock.co.zw/v1/ledger/user/{userId}/accounts" \
-H "x-api-key: sl_live_a1b2c3d4e5f6g7h8i9j0..." \
-H "x-company-id: cmp_01HXYZ..."Keys are prefixed sl_live_ (production) or sl_test_ (staging) for easy identification in your logs.
You can have multiple active keys per company. Rotate by generating a new key and then revoking the old one.
We store a SHA-256 hash of your key. We cannot recover it for you — only you know the raw value.
Never expose your API key in client-side code (browser JavaScript, mobile apps, or public repositories). All calls must originate from your server. A leaked key gives full access to all company funds and user data.
Bearer Token (Dashboard / Admin)
The SwitchLock dashboard and admin flows use short-lived JWTs. You obtain one by logging in as a Company Administrator. These tokens are scoped to the admin's company and role, and expire after a short window.
curl -X POST "https://api.switchlock.co.zw/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@yourcompany.co.zw",
"password": "YourPassword"
}'{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // short-lived (~15 min)
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // longer-lived
"userId": "adm_01HXYZ..."
}curl -X GET "https://api.switchlock.co.zw/v1/core/companies/me" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "x-company-id: cmp_01HXYZ..."curl -X POST "https://api.switchlock.co.zw/v1/auth/refresh" \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": "adm_01HXYZ..."
}'For server-to-server integrations, use API keys — not Bearer tokens. Tokens are intended for admin dashboards and browser sessions. They expire and require refresh cycles which add complexity to server code.
Company Context — x-company-id
SwitchLock is multi-tenant. Almost every endpoint that reads or writes data requires a company scope. The x-company-id header tells the API which tenant's data to operate on.
Super Admins can pass any x-company-id to operate across tenants. Regular API keys are tied to a single company and the header must match — mismatches return 403 Forbidden.
Your company ID is returned in the response when you register (POST /v1/core/companies) and in every login response.
Idempotency Keys
Financial mutations are risky to retry — a network timeout does not mean the request failed. Idempotency keys solve this: send the same key on a retry and SwitchLock returns the original response without executing the operation again.
Use a UUID v4 generated per-request. Keys expire after 24 hours.
| Endpoint | Key Required? |
|---|---|
| POST /v1/payments/deposit | Required |
| POST /v1/payments/generate-escrow-payment-link | Required |
| POST /v1/escrow/:id/release | Required |
| POST /v1/treasury/withdraw | Required |
| POST /v1/treasury/bank/capture | Required |
| POST /v1/escrow/initiate | Recommended |
If you send a request without an idempotency key to a protected endpoint, the API returns 400 Bad Request — x-idempotency-key header is required. Always generate a fresh UUID per operation, not per session.
Public Endpoints
Some endpoints are intentionally public — they are designed to be called from a buyer's browser or WhatsApp integration where no server secret is available.
| Endpoint | Purpose |
|---|---|
| POST /v1/auth/login | Admin dashboard login |
| POST /v1/auth/refresh | Refresh access token |
| POST /v1/core/companies | Register a new company (tenant) |
| GET /v1/payments/link-details/:hash | Get payment link details for checkout page |
| POST /v1/payments/request-checkout-otp | Request OTP for buyer verification |
| POST /v1/payments/register-checkout-user | Self-register a buyer at checkout |
| POST /v1/payments/accept-payment | Buyer accepts and funds the payment link |
| POST /v1/payments/eft-pay/initiate | Initiate EFT payment from checkout page |
| POST /v1/payments/eft-pay/webhook | EFT provider webhook (Oppwa/HyperPay) |
| GET /v1/whatsapp/webhook | Meta webhook verification challenge |
| POST /v1/whatsapp/webhook | Incoming WhatsApp message handler |
| GET /health | Health check |
Authentication Errors
Missing or invalid API key / Bearer token. Check that your x-api-key header is present and the key has not been revoked.
Your key is valid but does not have permission for this operation. Commonly seen when the x-company-id doesn't match the key's company.
A required header is missing, most commonly x-idempotency-key on a financial mutation endpoint.