Skip to main content

API Keys

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

  1. Open the Joryio dashboard and go to Settings → API Keys.
  2. Click Create API Key.
  3. Give the key a name and (optionally) a description teammates will see.
  4. Optionally add an IP allowlist - a comma- or space-separated list of CIDRs or single IPs. Leave blank to allow any IP.
  5. Tick the permissions this key needs. Pick the smallest set that works - see the scope catalog below.
  6. Click Create key.
One-time display

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:

VerbMeaningExample
readList or fetch existing recordsusers:read, campaigns:read
writeCreate or update recordsusers:write, segments:write
sendTrigger a delivery / send actioncampaigns:send
deletePermanently remove recordsusers:delete, campaigns:delete
trackSend analytics eventsevents:track
activateStart/pause/resume a live workflowcanvas:activate
aliasAttach/detach alternative identifiersusers:alias
mergeCombine two user profilesusers:merge
exportBulk export recordsusers:export

Below is the complete public catalog - it is also returned by GET /api-keys/permissions:

Events

ScopeWhat it allows
events:trackSend custom events from your servers or SDK.
events:readQuery events that have been tracked.

Users

ScopeWhat it allows
users:readLook up user profiles and attributes by ID.
users:writeCreate or update user attributes and properties.
users:deletePermanently remove user profiles (GDPR / right-to-erasure).
users:aliasAttach or detach external IDs and email aliases to a user.
users:mergeCombine two user profiles into one.
users:exportBulk export user profiles for offline analysis.

Campaigns

ScopeWhat it allows
campaigns:readList campaigns and view their configuration.
campaigns:writeCreate or edit campaign drafts via API.
campaigns:sendTrigger a campaign send to a specific user or segment.
campaigns:deletePermanently remove campaigns from this workspace.

Segments

ScopeWhat it allows
segments:readList segments and view membership counts.
segments:writeCreate or update segment definitions.
segments:deletePermanently delete segments and their history.

User Journeys

ScopeWhat it allows
canvas:readList user journeys and inspect their step graph.
canvas:writeCreate or edit user journey drafts.
canvas:activateStart, pause, or resume a live user journey.
canvas:deleteRemove user journeys and their history.

Templates

ScopeWhat it allows
templates:readFetch email, SMS, and push template content.
templates:writeCreate or edit reusable message templates.
templates:deletePermanently delete templates from Brand Studio.

Subscriptions

ScopeWhat it allows
subscriptions:readView a user's opt-in state across channels.
subscriptions:writeSubscribe or unsubscribe users from groups and channels.

Apps & SDK Keys

ScopeWhat it allows
apps:readList the apps and SDK keys registered to this workspace.
apps:writeAdd, rotate, or remove SDK keys for mobile and web apps.

Asset Library

ScopeWhat it allows
assets:readFetch images, fonts, and other shared media.
assets:writeUpload, rename, or delete files in the asset library.

Entities

ScopeWhat it allows
entities:readQuery entity records (products, articles, places…).
entities:writeCreate or update entity records and properties.

Analytics

ScopeWhat it allows
analytics:readPull aggregated metrics, funnels, and report data.

Deliverability

ScopeWhat it allows
email_suppression:readInspect the email suppression list (bounces, complaints, manual).
email_suppression:writeAdd or remove entries from the email suppression list.
sms_suppression:readInspect the SMS suppression list (STOP responses, fails).
sms_suppression:writeAdd or remove phone numbers from the SMS suppression list.

Frequency Caps

ScopeWhat it allows
touching_rules:readInspect frequency-cap rules and their current counters.
touching_rules:writeCreate, edit, or remove frequency-cap rules.

WhatsApp

ScopeWhat it allows
whatsapp:readRead WhatsApp Business account configuration.
whatsapp:writeUpdate WhatsApp Business account configuration.

AI Agents

ScopeWhat it allows
ai_agents:readList AI agents and their configuration.
ai_agents:writeCreate 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:

MethodEndpointDescription
GET/api-keys/permissionsList all grantable scopes
POST/api-keysCreate a key (?environment=live or test; body: name, description?, permissions[], ipAllowlist?, expiresAt?) - the full key value is returned once
GET/api-keysList keys for the workspace
GET/api-keys/:apiKeyIdGet one key's metadata
PUT/api-keys/:apiKeyIdUpdate name, description, permissions, IP allowlist, or expiry
POST/api-keys/:apiKeyId/revokeDeactivate a key (it stops authenticating immediately)
POST/api-keys/:apiKeyId/rotateGenerate a new key value, keeping the same permissions - the new value is returned once
DELETE/api-keys/:apiKeyIdPermanently 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

StatusMessageWhat to check
401Invalid API key formatHeader missing, malformed, or doesn't start with jry_.
401Invalid or expired API keyKey was revoked, deleted, or its expiresAt passed.
401Request IP is not allowed for this API keySource IP didn't match any allowlist entry - see IP allowlist.
403This API key does not have the required permissions: ...Key is valid but doesn't have the scope required for the endpoint.
403ORG_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.