> ## Documentation Index
> Fetch the complete documentation index at: https://docs.davazmysel.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Links via API

> Mint a governed marketing link from your own backend or automation, with idempotent retries and destination controls.

Every link the dashboard UI can create, your backend can create too. The Machine Create API runs
the same interview and governance rules as a human filling in the **Add Mapping** dialog — no
caller gets its own shortcut taxonomy.

<Note>
  If you're building an AI assistant integration rather than calling the HTTP API directly, see
  [MCP Tools](/api/mcp-tools) — it wraps this same API for tools like Claude Desktop.
</Note>

## Authentication

Every request needs a Bearer API key with the **Links** scope.

<Steps>
  <Step title="Open API Keys">
    Go to **Settings → Workspace → Settings & Integrations**, find the **API Keys** card, and
    click **Create API Key**.
  </Step>

  <Step title="Select the Links scope">
    Give the key a name and choose an environment. **Events** is included on every key by
    default; check **Links** as well to allow this key to create and look up links.
  </Step>

  <Step title="Copy the plaintext key">
    The key is shown once. If you need to add the Links scope to a key you already created,
    open its row menu and edit its scopes — you don't need to issue a new key.
  </Step>
</Steps>

```
Authorization: Bearer dava_sk_live_...
```

A key without the Links scope gets `403 Forbidden` from both endpoints below.

## Create a link

```http theme={null}
POST https://api-v2.davazmysel.com/v2/links/generate
Authorization: Bearer dava_sk_live_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "link_type_id": "58d8461a-4655-419d-9e2f-634a4fb2a667",
  "campaign": "spring-sale",
  "destination": "https://yourcompany.com/products",
  "external_id": "order_evt_9f2a1",
  "source": "meta",
  "medium": "paid-social",
  "platform": "meta-ads"
}
```

### Request fields

| Field | Type | Required | Notes |
| - | - | - | - |
| `link_type_id` | UUID | Yes | The Link Type to mint from — the same ones your dashboard's **Add Mapping** dialog lists. |
| `campaign` | string | Yes | Campaign name, max 500 chars. |
| `destination` | string | Yes | Must be a valid `https://` URL. Plain `http://` and any URL containing embedded credentials (`user@host`) are rejected with `422` — both would expose visitors to interception or point somewhere other than what it appears to. |
| `distinguisher` | string | No | Optional variant/creative label, max 255 chars. |
| `external_id` | string | No | Your own idempotency key, max 255 chars. See [Idempotent retries](#idempotent-retries) below. |
| `source` / `medium` / `platform` / `objective` | string | No | Raw attribution values. If your workspace has an active governance policy, these are validated and normalized the same way a human's dashboard submission is — see [Governance enforcement](#governance-enforcement). |
| `stamp_utms` | boolean | No | Default `true`. When on, `issued_url` carries a `?utm_source=...&utm_medium=...&utm_campaign=...` query string built from the link's resolved attribution. Set `false` to mint a link with no query string. Applies only to the new link — never rewrites an existing mapping. |

### Response — created (201) or idempotent hit (200)

```json theme={null}
{
  "mapping": {
    "id": "77e0221a-52d4-420a-9a8a-e40491bf5dbf",
    "url_pattern": "/ref/spring-sale",
    "redirect_url": "https://yourcompany.com/products",
    "creation_source": "machine-api",
    "external_id": "order_evt_9f2a1",
    "utm_stamped": true
  },
  "issued_url": "https://yourdomain.com/ref/spring-sale?utm_source=meta&utm_medium=paid-social&utm_campaign=spring-sale",
  "idempotent": false,
  "enforcement": {
    "action": "pass",
    "violations": [],
    "resolved": { "source": "meta", "medium": "paid-social", "platform": "meta-ads", "objective": null }
  },
  "warning": null
}
```

A `201` means a new link was minted. A `200` with `idempotent: true` means the same `external_id`
was already used — you get the existing link back, not a duplicate.

## Find a link

Look up a link you created earlier by the `external_id` you gave it.

```http theme={null}
GET https://api-v2.davazmysel.com/v2/links?external_id=order_evt_9f2a1
Authorization: Bearer dava_sk_live_xxxxxxxxxxxxxxxxxxxx
```

```json theme={null}
{
  "mapping": {
    "id": "77e0221a-52d4-420a-9a8a-e40491bf5dbf",
    "url_pattern": "/ref/spring-sale",
    "destination_url": "https://yourcompany.com/products",
    "creation_source": "machine-api",
    "external_id": "order_evt_9f2a1"
  },
  "issued_url": "https://yourdomain.com/ref/spring-sale"
}
```

An unknown `external_id` returns `404`, not an error you need to treat specially — it just means
nothing has been created with that key yet.

## Idempotent retries

Always send the same `external_id` when retrying a create call after a timeout or network error.
The API compares it against links already created under your account — a second call with the
same key returns the original link (`200`, `idempotent: true`) instead of creating a duplicate.
If you omit `external_id`, a new one is generated automatically for you, and a bare retry (no
`external_id` on either call) will mint a second link.

## Governance enforcement

If your workspace has published a governance version (**Settings → Governance**), the
`source`/`medium`/`platform`/`objective` you send are checked against it, the same as a value
typed into the dashboard's **Add Mapping** dialog. Unrecognized values can be blocked depending on
your workspace's policy:

```json theme={null}
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Link creation blocked by governance enforcement",
  "violations": [
    { "field": "source", "raw": "fb", "approved": ["facebook", "instagram", "..."] }
  ]
}
```

Workspaces without an active governance version skip this check entirely — every value passes
through as sent.

## Destination allowlist

An account can optionally restrict which destinations API-created links may point to
(**Settings → Workspace → Settings & Integrations**). When set, a `destination` outside the
allowed origins is rejected:

```json theme={null}
{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "destination is not in the account allowlist",
  "destination": "https://not-allowed.example.com/page"
}
```

No allowlist configured means any `https://` destination is accepted.

## Audit trail

Every successful create is recorded with the API key that made it, the raw values sent, and the
values governance resolved them to — visible to your workspace admins for compliance and
debugging. Idempotent replays are not re-logged; only the original creation is.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.