---
title: "REST"
description: "Track server-side customer events with Gleap REST requests. Learn the required headers, request format and event properties."
url: https://docs.gleap.ai/en/articles/708-rest
collection: "Server-side event tracking"
updated: 2026-11-01
language: en
---

# REST

Track server-side customer events with Gleap REST requests. Learn the required headers, request format and event properties.

This package allows you to track server-side customer events.

## Base URL

The API host depends on the [data region](https://gleap-developer-docs.gleap.help/en/articles/711-data-regions) of your project:

<table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Data region</p></th><th colspan="1" rowspan="1"><p>API host</p></th></tr><tr><td colspan="1" rowspan="1"><p>EU (default)</p></td><td colspan="1" rowspan="1"><p><code>https://api.gleap.io</code></p></td></tr><tr><td colspan="1" rowspan="1"><p>US</p></td><td colspan="1" rowspan="1"><p><code>https://api.us.gleap.ai</code></p></td></tr></tbody></table>

> **Info:** The examples on this page use the EU host. If your project lives in another region, replace `https://api.gleap.io` with your region's host. API tokens are per project and only valid in the project's region.

## Identify users

```javascript
POST https://api.gleap.io/admin/identify
```

**Request headers:**

```javascript
Content-Type: application/json
Api-Token: YOUR_SECRET_API_TOKEN
```

Please replace _YOUR\_SECRET\_API\_TOKEN_ with your project's identity verification secret, found in **Settings → Security → User identity** (the same secret that signs the user hash). Keep it on your server. The server API needs the Team plan or higher.

**Request content:**

```javascript
{
    "userId": "19283",
    "name": "John Doe",
    "email": "john@doe.com",
    "value": 1928,
    "phone": "+1 (129) 18283 8292",
    "createdAt": "2022-09-28T10:11:18.156Z",
    "companyId": "acme-inc",
    "companyName": "ACME inc.",
    // You can also track custom user properties
}
```

The optional `value` field is the contact's MRR: monthly recurring revenue, in your billing currency, in major units.

Set `companyId` to associate the user with a company. The optional `companyName` is a fallback label and never overwrites a name set authoritatively via the Companies endpoint (below).

### Last activity update

By default the last activity gets updated to `new Date()`. You can prevent this by adding `?preventLastActivityUpdate=true` to your request params.

```javascript
POST https://api.gleap.io/admin/identify?preventLastActivityUpdate=true
```

## Merge contacts

```javascript
POST https://api.gleap.io/admin/contacts/merge
```

Merges contacts **without** a user ID (for example a contact created by an email to your support address, or a website visitor) into a user. Their conversations, messages and survey answers move to the user. Uses the same `Api-Token` header as above.

**Request content:**

```javascript
{
    "into": { "userId": "19283" },
    "from": { "email": "john@doe.com" }
}
```

-   `from.email` merges every contact without a user ID that has this email. `from.id` merges one contact by its Gleap ID instead.
    
-   A contact that has its own user ID is never merged (400). An unknown user or contact answers 404.
    
-   Fields the user doesn't have yet (name, phone, company, custom data) are taken over from the merged contacts.
    

**Response:**

```javascript
{ "merged": 2, "into": "6650f0..." }
```

## Companies

Create, update, read and delete companies. Companies hold authoritative attributes (plan, value, SLA, address and custom data) shown in the dashboard and used for company-level SLAs. All endpoints use the same `Api-Token` header as above.

### Create or update a company

```javascript
PUT https://api.gleap.io/admin/companies/{companyId}
```

`{companyId}` is your own immutable identifier for the company (URL-encode it) — the same id you pass to identify as `companyId`. The company is created on first use and updated on subsequent calls.

**Request headers:**

```javascript
Content-Type: application/json
Api-Token: YOUR_SECRET_API_TOKEN
```

**Request content:**

```javascript
{
    "name": "ACME inc.",
    "plan": "Growth plan",
    "value": 4990,
    "sla": 3600,
    "domain": "acme.com",
    "address": {
        "line1": "1 Infinite Loop",
        "city": "Cupertino",
        "state": "CA",
        "postalCode": "95014",
        "country": "US"
    },
    "customData": {
        "tier": "gold"
    }
}
```

`sla` is the response-time SLA in seconds. Unknown fields are ignored; `companyId` cannot be set from the body. Returns the saved company.

`value` is the company's MRR: monthly recurring revenue, in your billing currency, in major units. Kai Copilot weighs a company account with the maximum of the company `value` and its members' contact `value`s, counted once per company. Update it from your billing webhooks; set `0` on cancellation.

### Create or update many companies

```javascript
POST https://api.gleap.io/admin/companies/bulk
```

Writes up to 1000 companies in one call, for example a nightly sync from your customer database. Each entry is written exactly like the single `PUT /admin/companies/{companyId}` above, with `companyId` inside the entry. Send larger lists in several calls; re-sending entries is safe.

**Request content:**

```javascript
{
    "companies": [
        {
            "companyId": "LIZ123",
            "name": "ACME inc.",
            "plan": "Gold",
            "sla": 3600,
            "tags": ["kplus", "dms"],
            "ownerEmail": "account-manager@yourcompany.com",
            "customData": {
                "kplusVersion": "2026.2"
            }
        },
        {
            "companyId": "LIZ124",
            "name": "Beta GmbH"
        }
    ]
}
```

**Response:**

```javascript
{
    "imported": 2,
    "invalidRows": [],
    "failed": []
}
```

-   Entries with the same `companyId` are merged; the last one wins.
    
-   `invalidRows` lists the positions (starting at 0) of entries without a usable `companyId`. Nothing is written for them.
    
-   `failed` lists companies that could not be saved, with a message. Send them again.
    
-   Unlike the single endpoint, a bulk write does not update the company's "last seen" time.
    
-   More than 1000 entries in one call answers `400`.
    

### Get a company

```javascript
GET https://api.gleap.io/admin/companies/{companyId}
```

Returns the company, or `404` if it does not exist.

### Delete a company

```javascript
DELETE https://api.gleap.io/admin/companies/{companyId}
```

Permanently deletes the company. Its members (contacts) and their conversations are not deleted.

## Pipelines (CRM)

Manage CRM pipeline entries from your backend: put a company or contact on a pipeline, move it through stages, set field values, or remove it. All endpoints use the same `Api-Token` header as above.

Entries are addressed by your own identifiers — the `companyId` you pass to the Companies endpoints for company pipelines, or the `userId` you pass to identify for contact pipelines (URL-encode both). Whether a pipeline holds companies or contacts is its `recordType`.

### List pipelines

```javascript
GET https://api.gleap.io/admin/pipelines
```

Returns every pipeline of the project with its stages and fields — everything needed for the entry endpoints below:

```javascript
[
    {
        "id": "665f1c2e8b4d2a0012345678",
        "name": "Onboarding",
        "recordType": "COMPANY",
        "stages": [{ "id": "stage-id", "name": "New", "color": "#6B7CFF" }],
        "fields": [{ "fieldId": "dealsize", "label": "Deal size", "type": "CURRENCY", "currency": "USD" }]
    }
]
```

Stage ids are the same ids the v3 API calls `laneId` (a pipeline's `lanes`) — if you use both surfaces, they are interchangeable.

### Add a record to a pipeline

```javascript
POST https://api.gleap.io/admin/pipelines/{pipelineId}/companies/{companyId}
POST https://api.gleap.io/admin/pipelines/{pipelineId}/contacts/{userId}
```

**Request content (all fields optional):**

```javascript
{
    "stageId": "stage-id",
    "values": { "dealsize": 4990 }
}
```

`stageId` defaults to the pipeline's first stage. `values` are keyed by `fieldId`; values must be primitives (string, number, boolean or null). Returns `201` with the created entry. Adding is idempotent: if the record is already on the pipeline, the existing entry is returned unchanged with `200` — use the PUT endpoint to also update an existing entry. A genuine add runs the pipeline's automations, exactly like the same action in the dashboard.

### Create or update an entry

```javascript
PUT https://api.gleap.io/admin/pipelines/{pipelineId}/companies/{companyId}
PUT https://api.gleap.io/admin/pipelines/{pipelineId}/contacts/{userId}
```

Same body as above. If the record is not on the pipeline it is added; otherwise its entry is updated: `stageId` moves it (running the stage's automations), and `values` are merged — fields you do not send are left untouched, `null` clears a field.

**Response:**

```javascript
{
    "id": "665f1d4a8b4d2a0087654321",
    "pipelineId": "665f1c2e8b4d2a0012345678",
    "recordType": "COMPANY",
    "companyId": "acme-inc",
    "stageId": "stage-id",
    "values": { "dealsize": 4990 },
    "createdAt": "2026-08-12T09:00:00.000Z",
    "updatedAt": "2026-08-12T09:00:00.000Z"
}
```

### Get an entry

```javascript
GET https://api.gleap.io/admin/pipelines/{pipelineId}/companies/{companyId}
GET https://api.gleap.io/admin/pipelines/{pipelineId}/contacts/{userId}
```

Returns the entry, or `404` if the record is not on the pipeline.

### Remove an entry

```javascript
DELETE https://api.gleap.io/admin/pipelines/{pipelineId}/companies/{companyId}
DELETE https://api.gleap.io/admin/pipelines/{pipelineId}/contacts/{userId}
```

Removes the record from the pipeline. The company or contact itself is kept.

### Errors

Unknown pipelines, records or entries return `404`. An unknown `stageId`, an unknown `values` key or a non-primitive value returns `400` with a message naming the valid ids. Using a companies URL on a contact pipeline (or vice versa) returns `400` with the correct route.

## Track events

```javascript
POST https://api.gleap.io/admin/track
```

**Request headers:**

```javascript
Content-Type: application/json
Api-Token: YOUR_SECRET_API_TOKEN
```

**Request content:**

```javascript
{
    "events": [{
            "date": "2022-09-28T10:11:18.156Z",
            "name": "Subscription started",
            "data": {
                "name": "Sub 1232",
                "value": 1200
            },
            "userId": "19283"
        }, {
            "date": "2022-09-28T10:11:18.156Z",
            "name": "Subscription canceled",
            "data": {
                "name": "Sub 1259",
                "value": 1200,
                "reason": "Stopped working on the project.",
            },
            "userId": "19283"
        }]
}
```

## Rate limit

Please note that the identify and track APIs enforce a rate limit of 1500 requests / 60 seconds per API token. On the October 2026 plans this is the Team limit; Pro allows 4,500 and Enterprise 15,000 (see [Plans and AI pricing](https://gleap-developer-docs.gleap.help/en/articles/713-plans-and-ai-pricing#api-and-mcp-limits)). Starter has no API access (Team and up). If you exceed the limit, requests are rejected with `429 Too Many Requests` and the token is blocked for a short period before requests are accepted again.

## Related

- [NodeJS](https://docs.gleap.ai/en/articles/707-nodejs.md)
