Every call into the Joryio REST API is authenticated with an API key. Keys are scoped to a single workspace, carry an explicit list of permissions, and can be restricted to a set of IP addresses.
All endpoints on this page are relative to the base URL: https://api-eu1.joryio.com - see API Overview.
Creating a key
- Open the Joryio dashboard and go to Settings → API Keys.
- Click Create API Key.
- Give the key a name and (optionally) a description teammates will see.
- Optionally add an IP allowlist - a comma- or space-separated list of CIDRs or single IPs. Leave blank to allow any IP.
- Tick the permissions this key needs. Pick the smallest set that works - see the scope catalog below.
- Click Create key.
The full key value is shown exactly once, right after creation, and never again. Copy it into your secret manager (1Password, Vault, AWS Secrets Manager, etc.) before closing the dialog. If you lose it, delete the key and create a new one - Joryio cannot recover the original.
The key format is jry_live_<random> for production keys and jry_test_<random> for test keys. The key's visible prefix (jry_live_abc123) shows up in the dashboard's key list and in server logs, so you can identify which key did what without exposing the full value.
Using a key
Send the key as a bearer token in the Authorization header on every request:
GET /users/by-user-id/user_123
Host: api-eu1.joryio.com
Authorization: Bearer jry_live_98f31a72…
Content-Type: application/json
Scope catalog
Permissions follow a <resource>:<verb> shape. The verbs in use:
| Verb | Meaning | Example |
|---|
read | List or fetch existing records | users:read, campaigns:read |
write | Create or update records | users:write, segments:write |
send | Trigger a delivery / send action | campaigns:send |
delete | Permanently remove records | users:delete, campaigns:delete |
track | Send analytics events | events:track |
activate | Start/pause/resume a live workflow | canvas:activate |
alias | Attach/detach alternative identifiers | users:alias |
merge | Combine two user profiles | users:merge |
export | Bulk export records | users:export |
Below is the complete public catalog - it is also returned by GET /api-keys/permissions:
Events
| Scope | What it allows |
|---|
events:track | Send custom events from your servers or SDK. |
events:read | Query events that have been tracked. |
Users
| Scope | What it allows |
|---|
users:read | Look up user profiles and attributes by ID. |
users:write | Create or update user attributes and properties. |
users:delete | Permanently remove user profiles (GDPR / right-to-erasure). |
users:alias | Attach or detach external IDs and email aliases to a user. |
users:merge | Combine two user profiles into one. |
users:export | Bulk export user profiles for offline analysis. |
Campaigns
| Scope | What it allows |
|---|
campaigns:read | List campaigns and view their configuration. |
campaigns:write | Create or edit campaign drafts via API. |
campaigns:send | Trigger a campaign send to a specific user or segment. |
campaigns:delete | Permanently remove campaigns from this workspace. |
Segments
| Scope | What it allows |
|---|
segments:read | List segments and view membership counts. |
segments:write | Create or update segment definitions. |
segments:delete | Permanently delete segments and their history. |
User Journeys
| Scope | What it allows |
|---|
canvas:read | List user journeys and inspect their step graph. |
canvas:write | Create or edit user journey drafts. |
canvas:activate | Start, pause, or resume a live user journey. |
canvas:delete | Remove user journeys and their history. |
Templates
| Scope | What it allows |
|---|
templates:read | Fetch email, SMS, and push template content. |
templates:write | Create or edit reusable message templates. |
templates:delete | Permanently delete templates from Brand Studio. |
Subscriptions
| Scope | What it allows |
|---|
subscriptions:read | View a user's opt-in state across channels. |
subscriptions:write | Subscribe or unsubscribe users from groups and channels. |
Apps & SDK Keys
| Scope | What it allows |
|---|
apps:read | List the apps and SDK keys registered to this workspace. |
apps:write | Add, rotate, or remove SDK keys for mobile and web apps. |
Asset Library
| Scope | What it allows |
|---|
assets:read | Fetch images, fonts, and other shared media. |
assets:write | Upload, rename, or delete files in the asset library. |
Entities
| Scope | What it allows |
|---|
entities:read | Query entity records (products, articles, places…). |
entities:write | Create or update entity records and properties. |
Analytics
| Scope | What it allows |
|---|
analytics:read | Pull aggregated metrics, funnels, and report data. |
Deliverability
| Scope | What it allows |
|---|
email_suppression:read | Inspect the email suppression list (bounces, complaints, manual). |
email_suppression:write | Add or remove entries from the email suppression list. |
sms_suppression:read | Inspect the SMS suppression list (STOP responses, fails). |
sms_suppression:write | Add or remove phone numbers from the SMS suppression list. |
Frequency Caps
| Scope | What it allows |
|---|
touching_rules:read | Inspect frequency-cap rules and their current counters. |
touching_rules:write | Create, edit, or remove frequency-cap rules. |
WhatsApp
| Scope | What it allows |
|---|
whatsapp:read | Read WhatsApp Business account configuration. |
whatsapp:write | Update WhatsApp Business account configuration. |
AI Agents
| Scope | What it allows |
|---|
ai_agents:read | List AI agents and their configuration. |
ai_agents:write | Create or edit AI agents and their provider settings. |
IP allowlist
You can pin a key to a fixed set of source IPs. When the allowlist is empty, requests from any IP are accepted (the default). When it has entries, only requests whose source IP matches at least one entry get through.
Accepted syntax
- Plain IPv4 address -
203.0.113.42
- IPv4 CIDR range -
10.0.0.0/24, 192.168.1.0/16
- IPv6 address - exact match only (no CIDR for IPv6 yet)
Combine multiple entries by comma-separating them in the dashboard:
10.0.0.0/24, 203.0.113.42, 2001:db8::1
Source IP resolution
The client IP is resolved from the edge network's trusted header (set fresh on every request at the edge - a client-supplied value cannot survive), falling back to the trusted-proxy connection address. The client-controlled leftmost X-Forwarded-For entry is deliberately not used, so the allowlist can't be spoofed. IPv4-mapped IPv6 addresses (::ffff:203.0.113.42) are normalized to their IPv4 form before matching.
Rejected request shape
When a request comes from a non-allowlisted IP, the API returns 401 Unauthorized with:
{
"statusCode": 401,
"message": "Request IP is not allowed for this API key",
"timestamp": "2026-05-12T08:14:00.000Z",
"path": "/users"
}
The rejection is logged server-side with the key prefix and the offending IP so you can audit it.
Key management endpoints
Keys are managed via the dashboard (Settings → API Keys) or the API:
| Method | Endpoint | Description |
|---|
| GET | /api-keys/permissions | List all grantable scopes |
| POST | /api-keys | Create a key (?environment=live or test; body: name, description?, permissions[], ipAllowlist?, expiresAt?) - the full key value is returned once |
| GET | /api-keys | List keys for the workspace |
| GET | /api-keys/:apiKeyId | Get one key's metadata |
| PUT | /api-keys/:apiKeyId | Update name, description, permissions, IP allowlist, or expiry |
| POST | /api-keys/:apiKeyId/revoke | Deactivate a key (it stops authenticating immediately) |
| POST | /api-keys/:apiKeyId/rotate | Generate a new key value, keeping the same permissions - the new value is returned once |
| DELETE | /api-keys/:apiKeyId | Permanently delete a key (returns 204) |
A caller can never grant a key scopes it doesn't hold itself.
List response fields
GET /api-keys returns { "apiKeys": [...] }, each key with the following shape (the full key value and its hash are never exposed):
{
"apiKeys": [
{
"id": "7c2e4f6a-1b3d-4e5f-8a9b-0c1d2e3f4a5b",
"name": "ServerSide Updates",
"description": "Used by our backend to send events.",
"keyPrefix": "jry_live_98f31a72",
"permissions": ["events:track", "users:write"],
"lastUsedAt": "2026-05-12T08:14:00.000Z",
"expiresAt": null,
"isActive": true,
"createdAt": "2026-02-19T12:00:00.000Z",
"updatedAt": "2026-02-19T12:00:00.000Z"
}
]
}
The key's ipAllowlist is set at create/update time; it is enforced on every request but not included in the list response.
Common errors
| Status | Message | What to check |
|---|
401 | Invalid API key format | Header missing, malformed, or doesn't start with jry_. |
401 | Invalid or expired API key | Key was revoked, deleted, or its expiresAt passed. |
401 | Request IP is not allowed for this API key | Source IP didn't match any allowlist entry - see IP allowlist. |
403 | This API key does not have the required permissions: ... | Key is valid but doesn't have the scope required for the endpoint. |
403 | ORG_HARD_SUSPENDED: organization is suspended. Read-only access only. | The owning organization has been suspended - contact support. |
Rotating a key
Call POST /api-keys/:apiKeyId/rotate (or use the dashboard): the key gets a new secret value (and a new keyPrefix) - returned exactly once in the response - while keeping its name, scopes, and ID. Anything still calling with the old value starts failing immediately, so deploy the new value first where you can, and use the keyPrefix in your access logs to find integrations still on the old key. If you prefer a zero-downtime rotation, create a second key with the same scopes, migrate callers, then delete the old key.