Skip to main content
POST https://api.craftkit.dev/v1/embed/sessions
Mint an embed session. Your backend calls this with a project API key; the response carries a signed EdDSA session JWT, the iframe_url to mount, and a single-use renew_token. Sessions live for 4 hours — rotate the token with Refresh a session before expires_at. The key’s project must have embed enabled, or auth returns invalid_credentials.

Authorization

Authorization
string
required
Bearer ck_live_… — a project API key for a project with embed enabled.

Body

tenant
object
required
The organization this session belongs to. Upserted on every mint.
actor
object
required
The end-user inside the iframe. Upserted under the tenant.
scope
object
default:"{ mode: 'edit' }"
What the session can open.
variableCatalog
object
An inline catalog used for this session only (same shape as Create a catalog). Mutually exclusive with catalogRef.
catalogRef
object
Reference a published catalog by name. Resolves to the current version when version is omitted.
permissions
object
Partial override of permission flags (publish, saveDraft, delete, rename, rollback, createCustomVariables, changePageSettings, viewVersionHistory, submitForm, saveFormDraft, shareDocument, emailDocument, viewEngagement). Omitted flags use schema defaults.
permissionsPreset
string
Name of a saved permission preset (≤60 chars). Accepted by the schema; reserved.
branding
object
Partial branding (primaryColor, logoUrl, fontUrl, locale, ui, support).
appearance
object
Framework-agnostic styling contract (baseTheme, variables, rules, layout, stylesheetUrl, fontUrl, logoUrl). Supersedes branding when both are present. Falls back to the partner’s default theme.
callbacks
object
onPublishedUrl / onCloseUrl — partner URLs the embed posts to.
limits
object
Partial override of maxPublishes (10), maxSaveDrafts (200), maxUploadsBytes (5 MiB).
form
object
Form-fill claims — only meaningful when scope.mode === 'fill': prefill, showPreview (false), showDocumentAfterSubmit (true), redirectUrl.
Send either variableCatalog (inline, one-off) or catalogRef (a pointer to a published catalog), not both. Neither is required — omit both for a session with no catalog. An unknown catalogRef.name returns 404 catalog_not_found.

Response

200 with the minted session.
session_id
string
Session UUID. Use it to revoke the session server-side.
session_token
string
Signed EdDSA JWT, also carried in iframe_url.
iframe_url
string
URL to mount in your <iframe> (builder, or form route in fill mode).
expires_at
string
ISO-8601 expiry, 4 hours from mint.
renew_token
string
Single-use token for the refresh endpoint.

Errors

StatuscodeMeaning
401missing_authorizationNo Authorization: Bearer header.
401invalid_credentialsKey not found, revoked, or embed not enabled.
400invalid_jsonBody is not valid JSON.
422invalid_requestBody failed schema validation (issues included).
404catalog_not_foundcatalogRef.name has no current catalog in this project.
500catalog_resolution_failedInline/ref catalog lookup threw — retry.
500mint_failedMinting threw (e.g. no active signing key) — retry.
cURL
curl -X POST https://api.craftkit.dev/v1/embed/sessions \
  -H "Authorization: Bearer $CRAFTKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant": { "externalId": "org_123", "displayName": "Acme Corp" },
    "actor":  { "externalId": "usr_456", "displayName": "Jane Smith", "email": "jane@acme.com" },
    "scope":  { "mode": "edit", "templateExternalId": "invoice" },
    "catalogRef": { "name": "my-catalog" },
    "permissions": { "publish": true, "saveDraft": true }
  }'
200
{
  "session_id": "0193c2c3-1a2b-7c3d-8e4f-aabbccddeeff",
  "session_token": "eyJhbGciOiJFZERTQS...",
  "iframe_url": "https://embed.craftkit.dev/embed/builder?session_token=eyJhbGciOiJFZERTQS...",
  "expires_at": "2026-06-05T14:00:00.000Z",
  "renew_token": "ert_8sR2...Xq"
}
Persist renew_token and expires_at server-side. Before expiry, call POST /v1/embed/sessions/refresh with the same project’s API key to rotate the session.