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

# Authentication

> Get your API credentials and make your first authenticated request.

<Warning>
  **Read [API Conventions](/guides/api-conventions) first.** It covers team scoping, the response
  envelope and error handling — the things that decide whether your requests come back with the
  records you expect. Most "why can't I get any of my records?" problems trace back to
  [team scoping](/guides/api-conventions#team-scoping), not to authentication.
</Warning>

ConveYour uses token-based authentication. Every API request needs one header — `x-conveyour-token` — containing an API key you create in Settings.

## Key types

There are two key types. Pick the right one for your use case — they have different permissions.

<CardGroup cols={2}>
  <Card title="Server-only — Full API" icon="server">
    Full access to all API endpoints. Use this for backend integrations, Zapier, server-side automation, and anything that runs outside the browser.

    **Never expose this key in frontend code or public repos.**
  </Card>

  <Card title="Client-safe — Analytics only" icon="chart-line">
    Restricted to `identify` and `track` endpoints only. Safe to embed in public-facing JavaScript — even if someone finds the key, they can only send analytics events.
  </Card>
</CardGroup>

***

## Create your credentials

API keys live on **Service Accounts** — purpose-built identities for integrations, not tied to any real user's login. This means you can revoke a key without touching anyone's account.

<Frame caption="Setting up an API service account and key (8:34)">
  <iframe className="w-full aspect-video rounded-xl" src="https://player.vimeo.com/video/1200886417?h=ac8a13b1a3" title="Setting Up API Service Account & Key" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />
</Frame>

Prefer to read? The same flow is written out below.

<Steps>
  <Step title="Go to Settings → API">
    Log in to ConveYour as an admin and navigate to **Settings → API**.
  </Step>

  <Step title="Create a Service Account">
    Under the **Service Accounts** tab, create a new account. Give it a descriptive name that reflects its purpose (e.g. `Zapier`, `Website Analytics`, `Backend Sync`).

    Assign the appropriate permissions and team scope for what this integration needs to do.
  </Step>

  <Step title="Create an API Key">
    Under the **API Keys** tab, create a new key. Select:

    * The **Service Account** it belongs to
    * The **key type** — `Server-only — Full API` or `Client-safe — Analytics only`
    * An **expiration** — `Never`, `90 days`, or `1 year`

    Copy the token **immediately** — it is displayed only once and cannot be retrieved again.
  </Step>
</Steps>

<Warning>
  The full token is shown only once at creation. Store it in a secure secrets manager before closing the dialog.
</Warning>

***

## Permissions

Two separate things decide whether a request succeeds. Getting a `403` with a valid token almost always means the second one.

<Steps>
  <Step title="The key type — which endpoint families you can reach">
    A `Client-safe — Analytics only` key reaches `identify` and `track` and nothing else. A `Server-only — Full API` key can reach every endpoint family.
  </Step>

  <Step title="The service account's permissions — which endpoints within them">
    Each endpoint checks a named permission against the **service account that owns the key**. A Server-only key on a service account with no permissions is rejected everywhere.
  </Step>
</Steps>

<Warning>
  A `Server-only — Full API` key is **not** enough on its own. If the service account is missing the permission an endpoint requires, the request fails with:

  ```json theme={null}
  {
    "status": "failed",
    "message": "Lacking necessary permission add_contacts",
    "data": { "permission": "add_contacts" }
  }
  ```

  Fix it by granting that permission to the service account in **Settings → API → Service Accounts**, not by creating a new key.
</Warning>

The permission each endpoint needs is listed on its reference page. For Contacts:

| Permission | Endpoints |
| - | - |
| `contacts` | `GET /api/contacts`, `GET /api/contacts/:id`, `PUT /api/contacts/:id`, `POST /api/contacts/resolve`, `POST /api/contacts/merge` |
| `add_contacts` | `POST /api/contacts` |
| `delete_contacts` | `DELETE /api/contacts/:id` |
| `import_contacts` | Contact import endpoints (deleting an import needs `destroy_import`) |
| *(none)* | `GET /api/contacts/metrics`, `archive`, `unarchive`, `block`, `unblock`, `compile`, `reveal-secret` |

<Note>
  The last row is not an omission. Those endpoints check no permission at all — any valid token reaches them, including `reveal-secret`. Scope your service accounts on the assumption that a working key can call them.
</Note>

<Note>
  Some permissions can never be granted to a service account, no matter how it is configured — `manage_billing`, `hooks`, `create_users`, `read_users`, `update_users`, and `delete_users`. Anything needing those has to run as a real user, not an API key.
</Note>

### Endpoints no API key can reach

A service account **cannot be an admin** — ConveYour rejects the assignment (`Service accounts cannot be assigned admin role`), and the role cannot be changed later. Combined with the never-grantable permissions above, **26 endpoints always return `403` for any key on a service account**, regardless of how you configure it.

<Note>
  **This applies to service-account keys — the only type you can create today.** A *legacy* key is bound to a real user rather than a service account, so if that user is an admin, the key reaches these endpoints normally. If an old integration calls them successfully, it is running on a legacy admin-owned key, and it will stop working the moment you migrate it to a service account. See [Legacy keys](#legacy-keys).
</Note>

| Module | Endpoints | Blocked by |
| - | - | - |
| Compliance | all 4 | admin only |
| Roles | all 5 | admin (or `update_users`) |
| Teams | `POST`, `PUT`, `DELETE`, `GET /{id}/related` | admin only |
| Org billing | `GET`/`PUT /api/org/billing`, `GET /api/org/invoices`, `PUT /api/org/plan` | `manage_billing` |
| Webhooks | all 4 | `hooks` |
| Senders | all 5 | admin only |

<Note>
  Reading Teams still works with a key — `GET /api/teams` and `GET /api/teams/{id}` are not admin-gated. Only the write operations and `related` are.
</Note>

***

## Make a request

Include your token in the `x-conveyour-token` header. That is all that is required.

```bash theme={null}
curl -X GET "https://<your-subdomain>.conveyour.com/api/contacts" \
  -H "x-conveyour-token: YOUR_TOKEN"
```

Replace `<your-subdomain>` with your organization's slug — the subdomain you sign in on. For example, if you sign in at `acme.conveyour.com`, your slug is `acme`.

<Note>
  The older `x-conveyour-appkey` header is still accepted for backward compatibility but is no longer required. New integrations should use token-only auth.
</Note>

### Four endpoints need no token at all

These reach their handler with **no authentication** — the `pid` in the URL is the only thing protecting them:

* `GET /api/exports/public/:pid` — export status
* `GET /api/exports/public/:pid/download` — downloads the full export file
* `GET /api/reports/shared/:pid` — a shared report
* `POST /api/reports/shared/:pid/:report_id/action/:action_key` — **runs an action** on a shared report

The last one is a write, not a read. It is limited to actions the report explicitly marks shared-safe — anything else returns `403 Action is not available on shared reports` — but anyone holding the `pid` can run those actions.

<Warning>
  Treat these `pid` values as secrets. Anyone who obtains one can download the data without a key, so avoid putting them in logs, analytics, referrer-visible URLs, or anywhere a third party could pick them up.
</Warning>

***

## Understand Teams before you go further

Authentication gets you in. **Teams decide what you can see once you are in** — and that trips people
up more than anything else in this API, because a correctly authenticated request can still come back
with fewer records than you expect.

Teams is a standard feature on ConveYour-enabled orgs. Most endpoints accept a `teams` parameter that
scopes the request, and teams control which contacts and resources are visible. If your org is on a
legacy program that does not use Teams, omit the parameter entirely.

```bash theme={null}
# Scope a request to two teams. The brackets are required.
curl -X GET "https://<your-subdomain>.conveyour.com/api/contacts?teams[]=TEAM_A&teams[]=TEAM_B" \
  -H "x-conveyour-token: YOUR_TOKEN"
```

<Warning>
  **Two things that fail silently.** `teams=A&teams=B` without brackets resolves to `B` alone, because
  PHP keeps only the last value for a repeated plain key. And any value that is not a valid
  24-character ObjectId is dropped without an error — a mistyped team ID behaves exactly as if no team
  had been sent. Both look like a permissions or missing-data bug rather than a bad request.
</Warning>

**Get your team IDs from [`GET /api/teams`](/api-reference/teams/list-teams).** That is the list to
scope against — admins receive every team in the org, while a non-admin key receives only the teams it
belongs to.

New to Teams? **[Teams in the Administration overview](/overview/administration#teams)** explains what
they are and why they change the results of almost every endpoint — start there. Then read
[Team scoping](/guides/api-conventions#team-scoping) in the API Conventions guide for the full
parameter behaviour, including how to send `teams` in a JSON body. For how teams work inside the app,
see [Teams Introduction](https://conveyour.com/help/contacts-teams-introduction) in the Help Center.

***

## Authentication header reference

| Header | Required | Description |
| - | - | - |
| `x-conveyour-token` | **Yes** | Your API key token |
| `x-conveyour-appkey` | No | Legacy header — accepted but no longer needed |
| `Content-Type: application/json` | For POST/PUT | Required when sending a request body |

***

## Response envelope

All API responses use the same envelope:

```json theme={null}
{
  "status": "ok",
  "message": "Human-readable description",
  "data": {}
}
```

| Field | Type | Description |
| - | - | - |
| `status` | `string` | `"ok"` on success, `"failed"` on error |
| `message` | `string` | Description of the result |
| `data` | `object` or `array` | The response payload |

<Warning>
  **A `200` does not mean the request succeeded.** Many endpoints return HTTP `200` with `"status": "failed"` in the body — for example creating a record that fails validation, or fetching an ID that does not exist.

  Branch on the `status` field, never on the HTTP code alone. Code like `if (response.ok)` will treat these failures as successes.
</Warning>

***

## Legacy keys

If you have existing API keys created before the API revamp, they continue to work and appear as **Legacy** in Settings → API. Migrate them to service accounts when convenient — there is no forced cutover.

<Warning>
  Older keys shown as **Zapier (Legacy)** are scoped to `read`, `write`, `update`, `delete` — they cannot call `identify` or `track`. If an analytics call returns `403` on a key that works fine for CRUD, this is why. Create a new `Client-safe — Analytics only` key instead. Only the two types above can be created today; the Zapier type is legacy and no longer offered.
</Warning>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Analytics" icon="chart-line" href="/api-reference/analytics/identify-a-contact">
    Use your Client-safe key to identify contacts and track events from the browser.
  </Card>

  <Card title="Contacts" icon="users" href="/api-reference/contacts/list-contacts">
    Create, retrieve, update, and manage contacts with your Server-only key.
  </Card>

  <Card title="Team scoping" icon="users-gear" href="/guides/api-conventions#team-scoping">
    How the `teams` parameter changes which records a request returns.
  </Card>

  <Card title="API Conventions" icon="book" href="/guides/api-conventions">
    Response envelope, error handling, pagination and usage limits.
  </Card>
</CardGroup>


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