---
title: "Create a deep link"
description: "POST /deep-links: Creates a one-time link that opens the Flume app with the user signed in."
url: https://docs.tryflume.ai/api/create-deep-link
updated: 2026-11-11
---

# Create a deep link

> POST /deep-links: Creates a one-time link that opens the Flume app with the user signed in.

Creates a one-time link that opens the Flume app with the user signed in.

```http
POST https://sandbox-api.tryflume.ai/partner/v1/deep-links
```

- Create it when the user clicks, and open it at once. It works once, within 7 days.
- `search_session_uuid` is required. Add `sku_uuid` to open a product from that search.
- A replay of the same `Idempotency-Key` returns `url: null`.

## Request

### Headers

| Header | Required | Description |
|---|---|---|
| `X-Flume-Partner-Key` | Yes | Your partner key. |
| `X-Flume-User-Token` | Yes | The access token of the user you call for. |
| `Idempotency-Key` | No | One value per user action, 1 to 255 characters. A retry with the same value returns the first response. |

### Body (`application/json`)

| Field | Type | Required | Description |
|---|---|---|---|
| `search_session_uuid` | string (UUID) | Yes | One of the user's searches. Without `sku_uuid`, the link opens its results. |
| `sku_uuid` | string (UUID) or null | No | A product to open. It must be a result of that search. |
| `quote_uuid` | string (UUID) or null | No | An instant quote to open, with `sku_uuid`. It must be `READY` and belong to the user. |
| `target` | enum or null | No | What the link opens: `product`, `search` or `quote`. Optional. Without it, the link opens the most specific id you send: `quote_uuid`, then `sku_uuid`, then the search. When set, it wins: `product` with a `quote_uuid` opens the product. `quote` needs `quote_uuid`, and `product` needs `sku_uuid`. Every id you send is checked, whatever `target` says. |
| `utm_content` | string or null | No | A name for the button that asked, for your own reporting. Max 64 characters. |
| `redirect_uri` | string or null | No | Where Flume sends the user back to. It must be an `https` URL on a redirect host agreed with Flume. Max 2,048 characters. |

### Sample

**curl**

```sh
curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/deep-links" \
  -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
  -H "X-Flume-User-Token: $FLUME_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f",
  "sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30"
}'
```

**Python**

```python
import os

import requests

headers = {
    "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"],
    "X-Flume-User-Token": os.environ["FLUME_USER_TOKEN"],
}

response = requests.post(
    "https://sandbox-api.tryflume.ai/partner/v1/deep-links",
    headers=headers,
    json={
        "search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f",
        "sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30",
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

**Node.js**

```js
const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/deep-links", {
  method: "POST",
  headers: {
    "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY,
    "X-Flume-User-Token": process.env.FLUME_USER_TOKEN,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f",
    "sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30"
  }),
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
console.log(await response.json());
```

## Returns (201)

Every field is always present. A field with no value is `null`.

| Field | Type | Description |
|---|---|---|
| `url` | string or null | The link. It works once, within 7 days. `null` when you replay an `Idempotency-Key`. |
| `expires_at` | string | When the link stops working, in UTC. |

**Response (201)**

```json
{
  "url": "https://app.tryflume.ai/?utm_source=acme&utm_medium=partner#/l/dlk_q3Vh8yT…",
  "expires_at": "2026-10-23T09:14:22Z"
}
```

## Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `EMBED_TOKEN_INVALID` | You sent an `Authorization` header and no `X-Flume-Partner-Key`. Flume read the call as one from its own iframe. |
| 401 | `PARTNER_KEY_INVALID` | The partner key is missing, unknown, revoked, expired, or from the other environment. On live, also a platform Flume has not enabled yet. |
| 401 | `USER_TOKEN_INVALID` | The user access token is unknown, revoked, another partner's, or replaced by a later verification. A verification in the iframe replaces it too. |
| 403 | `USER_ACCOUNT_CLOSED` | The user's email belongs to a Flume account that Flume has closed. |
| 403 | `USER_NOT_CONNECTED` | No `X-Flume-User-Token` header was sent. |
| 404 | `NOT_FOUND` | The thing does not exist, or is not this user's. A search id that is not a UUID also returns this code. |
| 404 | `SESSION_NOT_FOUND` | The search does not exist, or is another user's. |
| 409 | `IDEMPOTENCY_IN_PROGRESS` | The first call with this key is still running. |
| 422 | `IDEMPOTENCY_KEY_REUSED` | The key was already used with a different body. |
| 422 | `QUOTE_NOT_READY` | The quote is not `READY`. On `POST /save-item` it also covers a quote that expired, is not this user's, lacks the option you named, or is under that option's minimum order. |
| 422 | `SKU_NOT_IN_SEARCH` | The product was not a result of the search you named. |
| 422 | `VALIDATION_FAILED` | A field is missing, unknown or has an unusable value. `field` names it as a dotted path. Some refusals carry the detail in `details` and leave `field` empty. |
| 429 | `RATE_LIMITED` | Today's limit for this call is used up. `Retry-After` is the number of seconds until 00:00 UTC. |
| 500 | `INTERNAL` | Something failed on Flume's side. |

Every code, with its fix: [API error codes](https://docs.tryflume.ai/api/errors.md).

Guide: [Open Flume signed in](https://docs.tryflume.ai/guides/open-flume-signed-in.md).
