---
title: "Mint an embed token"
method: POST
path: "/embed/tokens"
tags: ["Embed"]
---

# Mint an embed token

`POST /embed/tokens`

## Request

- Base URL: `https://api.spurnow.com`
- URL: `https://api.spurnow.com/embed/tokens`
- Auth: HTTP bearer

Call from your server with a workspace API key. Returns a single-use `embedUrl` (valid 60 seconds, only from `origin`) that opens one WhatsApp conversation in an iframe as the given Spur user, for an 8-hour session. Creates the contact from `contact.number` if needed. `channelId` (or `conversationId`) is required unless the workspace has exactly one WhatsApp number. Rate limit: 600 tokens per minute per workspace.

## Request body

- CreateEmbedTokenDto
  - `user` EmbedTokenUserRefDto, required
    - `id` number: Spur user id. Send this or `email`.
    - `email` string: Email of the Spur user the frame acts as. Send this or `id`.
  - `contact` EmbedTokenContactRefDto
    - `number` string: Phone number in international format: + and the country code, e.g. +919876543210 (00919876543210 also works). If no contact exists, one is created. Send this or `id`.
    - `id` number: Spur contact id from an earlier response. Send this or `number`.
    - `firstName` string: Only used when a contact is created.
    - `lastName` string: Only used when a contact is created.
    - `email` string: Only used when a contact is created.
  - `conversationId` number: Open this conversation directly. It already identifies the WhatsApp number.
  - `channelId` number: Which of your WhatsApp numbers to open, with `contact`. Required when the workspace has more than one WhatsApp number.
  - `origin` string, required: Origin of the page that will show the frame. Must be on the allowed list.
  - `codeChallenge` string, required: BASE64URL(SHA-256(codeVerifier)), 43 characters (PKCE, S256). The CRM page makes a random `codeVerifier` for each frame and sends only this hash to your server; when the frame posts `spur:ready`, the page answers with `spur:verify` and the verifier. Without it the link opens nothing, so a leaked `embedUrl` is useless.
  - `ttl` number: Token lifetime in seconds. Default and maximum 60. This is the token's life, not the session's.

## Response `201`

- EmbedTokenResponseDto
  - `token` string, required: Single-use token. Already part of `embedUrl`.
  - `embedUrl` string, required: Set this as the iframe src. Works once, within 60 seconds, from `origin`, and only with the code verifier.
  - `expiresAt` string, date-time, required
  - `userId` number, required
  - `contactId` number, required: Store this on your CRM record and mint with it next time.
  - `conversationId` number, required: The conversation the frame opens (inboxId).
  - `channelId` number, required: The WhatsApp number the conversation is on.
  - `sessionTtl` number, required: Session length in seconds once the frame loads.

## Other responses

- `400`: Missing contact or conversation, missing channelId with more than one WhatsApp number, invalid number, or origin not on the allowed list
- `401`: Invalid or deleted API key
- `403`: The user's permissions don't cover the conversation
- `404`: User, contact, conversation or channel not found
- `409`: The user hasn't accepted their invite
- `429`: 600 tokens per minute per workspace

---

[API](https://skmtc.dev/spurnow/apis/spur-apis.md) - [All operations](https://skmtc.dev/spurnow/apis/spur-apis/llms.txt) - [OpenAPI document](https://skmtc.dev/spurnow/apis/spur-apis/revisions/27ca7bf002cb?raw)
