Authentication & API keys
Authenticate every request with sk_test_ and sk_live_ keys: creation, scopes, rotation, and best practices.
Every API request authenticates with an API key. Keys belong to an organization, carry configurable scopes, and come in two environments: test and live.
Anatomy of a key
sk_test_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345
│ │ │
│ │ └── 32 random characters
│ └── Environment: test (sandbox) or live (production)
└── Prefix: sk = secret key
| Prefix | Environment | Effect |
|---|---|---|
sk_test_ |
Test | Simulated carriers, virtual balance — no real money |
sk_live_ |
Live | Real labels, real charges to your wallet |
A sk_test_ key can never read or modify live data, and the reverse is also true. The isolation is total. See Test mode.
Send your key
Two equivalent options; use whichever your HTTP client prefers:
curl https://api.sendit.mx/v1/shipments \
-H "X-API-Key: sk_test_..."curl https://api.sendit.mx/v1/shipments \
-H "Authorization: Bearer sk_test_..."Create a key
From the dashboard (Settings → API keys → Create key) or via the API:
curl -X POST https://api.sendit.mx/v1/api-keys \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"name": "Online store integration",
"environment": "live",
"scopes": ["shipments:write", "shipments:read", "rates:read", "labels:write", "tracking:read"]
}'
Body parameters
namestring
A descriptive name for the key (to identify it in the dashboard).
stringenvironment?string
test (sandbox) | live (production).
stringtestscopes?string[]
The key's scopes. The full list is in Scopes.
string[]["*"]ipAllowlist?string[]
(Beta) IPs or CIDR blocks the key may be used from.
string[]The full key is shown exactly once in the response. If you lose it, you cannot recover it: generate a new one. In the dashboard you identify each key by its visible prefix (sk_live_aBcD...).
Each plan caps how many active keys you can have. Creating one more returns 403 PLAN_LIMIT_REACHED. The count excludes keys in their 24-hour rotation window. See plans & quotas.
Limit each key’s scope
Every key carries a list of scopes in the resource:action pattern. A key can only do what its scopes allow. Everything else returns 403.
{
"name": "Online store integration",
"scopes": [
"shipments:write",
"shipments:read",
"rates:read",
"labels:write",
"tracking:read"
]
}
Issue each key with the minimum privilege that integration needs. The full scope list and its semantics live in Scopes.
Rotate a key
- Generate a new key with the same scopes.
- Update your integration to use the new one.
- Revoke the old one.
Keep both active during the transition and watch the old key’s lastUsedAt field to confirm nothing still uses it before revoking.
Restrict by IP
Beta This feature is in beta. Its behavior may change before the final release.
Optionally, limit a key to an IP range with a CIDR list. A request from an IP outside the list is rejected even if the key is valid. Use it on live keys that should only run from your servers.
Dashboard users
Anyone signing in to the dashboard authenticates with a user session and operates under their role in the organization:
| Role | Can |
|---|---|
| VIEWER | Read-only |
| OPERATOR | Create and manage shipments, addresses, packages; buy labels |
| ADMIN | All of the above + members, settings, and API keys |
| OWNER | Everything + billing and plan |
Roles apply to people; scopes apply to keys. For server-to-server integrations, always use API keys.
Authentication errors
| Code | When it happens | How to resolve it |
|---|---|---|
401 UNAUTHORIZED |
Key missing or header malformed | Send X-API-Key or Authorization: Bearer sk_... |
401 INVALID_API_KEY |
Key doesn’t exist or was revoked | Check you copied the full key; generate a new one if revoked |
401 EXPIRED_API_KEY |
Key passed its expiration date | Generate a new key and update your integration |
403 INSUFFICIENT_SCOPE |
Key is valid but lacks scopes (listed in details) |
Add the needed scope or use a key with sufficient permissions |