REST API v1

Chatlane API Documentation

RESTful API reference for conversations, messages, contacts, and custom entity records. Uses API Key authentication via Laravel Sanctum.

Base URL
https://chatlane.io/api/v1/
⌘K
Try in Postman

Overview

The Chatlane API provides a RESTful interface for accessing and managing conversations, messages, contacts, and custom entity records. The API uses API Key authentication via Laravel Sanctum.

Base URL

All API endpoints are prefixed with /api/v1/:

https://chatlane.io/api/v1/

Authentication

The API uses Bearer token authentication. Include your API token in the Authorization header:

Authorization: Bearer {your-api-token}

Generating API Tokens

  1. Log in to your Chatlane account
  2. Navigate to Settings → API Tokens
  3. Click "Create Token"
  4. Enter a name for your token (e.g., "Zapier Integration")
  5. Select the team to scope the token to
  6. Copy the token value immediately (it will not be shown again)

Important: API tokens are scoped to a specific team. The token can only access data from that team.

Rate Limiting

API requests are rate-limited. Contact support if you need higher limits.

Response Format

Success Response

All successful responses return JSON with the following structure:

Single Resource:

{
  "data": {
    "id": 1,
    "name": "Example",
    "created_at": "2026-01-26T10:00:00Z"
  }
}

Collection (Paginated):

{
  "data": [
    {
      "id": 1,
      "name": "Example 1"
    },
    {
      "id": 2,
      "name": "Example 2"
    }
  ],
  "links": {
    "first": "https://chatlane.io/api/v1/resource?page=1",
    "last": "https://chatlane.io/api/v1/resource?page=2",
    "prev": null,
    "next": "https://chatlane.io/api/v1/resource?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 2,
    "path": "https://chatlane.io/api/v1/resource",
    "per_page": 50,
    "to": 50,
    "total": 100
  }
}

Error Response

Error responses follow this format:

{
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": {}
}

HTTP Status Codes

  • 200 - Success
  • 201 - Created
  • 400 - Bad Request
  • 401 - Unauthenticated
  • 403 - Forbidden
  • 404 - Not Found
  • 422 - Validation Error
  • 500 - Server Error

Endpoints

Conversations

List Conversations

GET /api/v1/conversations

List conversations with optional filtering.

Query Parameters:

  • created_after (string, ISO 8601) - Filter conversations created after this timestamp (for polling)
  • inbox_id (integer) - Filter by inbox ID
  • status (string) - Filter by status: active, pending, closed
  • contact_id (integer) - Filter entity-record conversations to ones where this contact has sent or received at least one message. Pass either contact_id or contact_email, not both. Returns an empty page when the contact does not exist in the team.
  • contact_email (string) - Same as contact_id but lookup by email — useful when the calling system stores the contact's email but not their Chatlane id. Matching is case-insensitive and trimmed.
  • limit (integer, default: 50, max: 100) - Number of results per page
  • offset (integer) - Pagination offset

Example Request:

curl -X GET "https://chatlane.io/api/v1/conversations?created_after=2026-01-26T00:00:00Z&status=active" \
  -H "Authorization: Bearer {your-token}"

Surfacing conversations for a single contact:

curl -X GET "https://chatlane.io/api/v1/[email protected]" \
  -H "Authorization: Bearer {your-token}"

Example Response:

{
  "data": [
    {
      "id": 1,
      "subject": "Customer inquiry",
      "status": "active",
      "is_read": false,
      "is_archived": false,
      "is_starred": false,
      "last_message_at": "2026-01-26T10:30:00Z",
      "inbox": {
        "id": 1,
        "name": "Support",
        "type": "standard"
      },
      "participant": {
        "id": 1,
        "name": "John Doe",
        "email": "[email protected]"
      },
      "assignee": null,
      "created_at": "2026-01-26T10:00:00Z",
      "updated_at": "2026-01-26T10:30:00Z"
    }
  ],
  "links": {...},
  "meta": {...}
}

Get Single Conversation

GET /api/v1/conversations/{id}

Get detailed information about a specific conversation, including messages.

Query Parameters:

  • contact_id / contact_email (optional) - On an entity-record conversation, pre-filter the embedded messages array to that contact's thread (sender or recipient_contact_id). The conversation itself is still returned in full.

Example Request:

curl -X GET "https://chatlane.io/api/v1/conversations/1" \
  -H "Authorization: Bearer {your-token}"

Fetching only one contact's thread within an entity conversation:

curl -X GET "https://chatlane.io/api/v1/conversations/[email protected]" \
  -H "Authorization: Bearer {your-token}"

Example Response:

{
  "data": {
    "id": 1,
    "subject": "Customer inquiry",
    "status": "active",
    "inbox": {...},
    "participant": {...},
    "messages": [
      {
        "id": 1,
        "body": "Hello, I need help with...",
        "type": "email",
        "delivery_status": "sent",
        "created_at": "2026-01-26T10:00:00Z"
      }
    ],
    "assignee": {...},
    "created_at": "2026-01-26T10:00:00Z",
    "updated_at": "2026-01-26T10:30:00Z"
  }
}

Create Conversation

POST /api/v1/conversations

Create a new conversation.

Request Body:

{
  "inbox_id": 1,
  "contact_id": 123,
  "subject": "New conversation",
  "body": "Initial message content",
  "status": "active",
  "tags": ["vip", "billing", "priority"]
}

For Entity Inboxes:

{
  "inbox_id": 2,
  "custom_entity_record_id": 456,
  "subject": "New conversation",
  "body": "Initial message content",
  "status": "active",
  "tags": ["shipment", "high_value"]
}

Example Request:

curl -X POST "https://chatlane.io/api/v1/conversations" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "inbox_id": 1,
    "contact_id": 123,
    "subject": "New conversation",
    "tags": ["vip", "billing"]
  }'

Notes:

  • The body field is optional on conversation creation.
  • If body is provided, an initial email message is created automatically with that content.
  • For full control over message delivery (type, draft status, channel, attachments, etc.), the recommended pattern is:
    1. Create the conversation (with or without body).
    2. Then create one or more messages using the Message endpoint:
      POST /api/v1/conversations/{conversationId}/messages.

Example Response:

{
  "data": {
    "id": 1,
    "subject": "New conversation",
    "status": "active",
    "tags": [
      {
        "id": 10,
        "name": "vip",
        "color": "#3b82f6"
      },
      {
        "id": 11,
        "name": "billing",
        "color": "#3b82f6"
      }
    ],
    "created_at": "2026-01-26T10:00:00Z"
  }
}

Messages

List Messages

GET /api/v1/messages

List messages with optional filtering.

Query Parameters:

  • created_after (string, ISO 8601) - Filter messages created after this timestamp (for polling)
  • conversation_id (integer) - Filter by conversation ID
  • inbox_id (integer) - Filter by inbox ID
  • type (string) - Filter by message type: email, sms, whatsapp, note, phone_call, facebook, instagram
  • contact_id / contact_email - On entity-record conversations, narrow to messages to or from this contact (sender or recipient_contact_id). Activity rows are excluded by this endpoint regardless.
  • limit (integer, default: 50, max: 100) - Number of results per page

Example Request:

curl -X GET "https://chatlane.io/api/v1/messages?conversation_id=1&type=email" \
  -H "Authorization: Bearer {your-token}"

Fetching one contact's thread within a shared entity conversation:

curl -X GET "https://chatlane.io/api/v1/messages?conversation_id=42&[email protected]" \
  -H "Authorization: Bearer {your-token}"

Example Response:

{
  "data": [
    {
      "id": 1,
      "body": "Hello, I need help with...",
      "type": "email",
      "delivery_status": "sent",
      "is_read": false,
      "conversation": {
        "id": 1,
        "subject": "Customer inquiry"
      },
      "sender": {
        "id": 1,
        "name": "John Doe",
        "email": "[email protected]"
      },
      "created_at": "2026-01-26T10:00:00Z"
    }
  ],
  "links": {...},
  "meta": {...}
}

Get Single Message

GET /api/v1/messages/{id}

Get detailed information about a specific message.

Example Request:

curl -X GET "https://chatlane.io/api/v1/messages/1" \
  -H "Authorization: Bearer {your-token}"

Example Response:

{
  "data": {
    "id": 1,
    "body": "Hello, I need help with...",
    "type": "email",
    "delivery_status": "sent",
    "is_read": false,
    "external_message_id": null,
    "attachments": [],
    "conversation": {
      "id": 1,
      "subject": "Customer inquiry",
      "status": "active"
    },
    "channel": {
      "id": 1,
      "name": "Email Channel",
      "type": "email",
      "is_active": true
    },
    "sender": {
      "id": 1,
      "name": "John Doe",
      "email": "[email protected]"
    },
    "created_at": "2026-01-26T10:00:00Z",
    "updated_at": "2026-01-26T10:00:00Z"
  }
}

Create Message

POST /api/v1/conversations/{conversationId}/messages

Create a new message in a conversation.

Request Body:

{
  "body": "Message content",
  "type": "email",
  "is_draft": false,
  "inbox_channel_id": 123,
  "file_urls": [
    "https://example.com/file1.pdf",
    "https://example.com/file2.jpg"
  ],
  "cc": "[email protected]",
  "bcc": "[email protected]",
  "sender_type": "contact",
  "sender_id": 456
}

Parameters:

  • body (required, string) - Message content
  • type (required, string) - Message type: email, sms, whatsapp, note, phone_call, facebook, instagram
  • is_draft (boolean, default: false) - If true, message is saved as draft. If false, message is sent immediately.
  • inbox_channel_id (integer, optional) - Specific channel to use. If not provided, uses first active channel of the specified type.
  • file_urls (array, optional) - Array of publicly accessible file URLs to download and attach
  • contact_id (integer, optional) - The contact the message is addressed to. Required for entity record conversations, except when sender_type is contact — a contact-authored message is inbound (addressed to your team, not to a peer), so it is recorded with no recipient.
  • record_only (boolean, default: false) - Log a message that was already sent or received by another system into the conversation history without dispatching delivery. Nothing is sent; the message is stored with delivery_status: sent.
  • cc (string, optional) - CC recipients (comma-separated)
  • bcc (string, optional) - BCC recipients (comma-separated)
  • sender_type (string, optional) - Type of sender: user, contact, or agent. If omitted, the sender defaults to the authenticated user.
  • sender_id (integer, optional) - ID of the sender model. Must belong to the same team. Required when sender_type is provided.

Example Request:

curl -X POST "https://chatlane.io/api/v1/conversations/1/messages" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Thank you for your inquiry!",
    "type": "email",
    "is_draft": false,
    "sender_type": "contact",
    "sender_id": 456
  }'

Notes:

  • If sender_type and sender_id are not provided, the message sender defaults to the authenticated user associated with the API token.
  • When specifying a custom sender:
    • Both sender_type and sender_id are required.
    • sender_type: "user"sender_id must be a user that belongs to the same team.
    • sender_type: "contact"sender_id must be a contact in the same team.
    • sender_type: "agent"sender_id must be an agent in the same team.

Recording an inbound message from a contact:

Use this to mirror something a contact did in your own system (a form submission, a quote, a reply you already emailed them about) into the Chatlane conversation, so your team can reply to them from the inbox.

curl -X POST "https://chatlane.io/api/v1/conversations/1/messages" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "<p>I have submitted my quote for this order.</p>",
    "type": "email",
    "record_only": true,
    "sender_type": "contact",
    "sender_id": 456
  }'

Omitting contact_id is what marks the message as inbound — recipient_contact_id stays null, exactly like a message that genuinely arrived from the contact. Pair it with record_only: true whenever your own system has already delivered the message, so Chatlane records it without sending anything.

Example Response:

{
  "data": {
    "id": 1,
    "body": "Thank you for your inquiry!",
    "type": "email",
    "delivery_status": "sending",
    "is_read": true,
    "external_message_id": null,
    "attachments": [],
    "conversation": {
      "id": 1,
      "subject": "Customer inquiry",
      "status": "active"
    },
    "channel": {
      "id": 1,
      "name": "Email Channel",
      "type": "email",
      "is_active": true
    },
    "sender": {
      "id": 2,
      "name": "Support Agent",
      "email": "[email protected]"
    },
    "created_at": "2026-01-26T10:00:00Z",
    "updated_at": "2026-01-26T10:00:00Z"
  }
}

Contacts

List Contacts

GET /api/v1/contacts

List contacts with optional filtering.

Query Parameters:

  • created_after (string, ISO 8601) - Filter contacts created after this timestamp (for polling)
  • limit (integer, default: 50, max: 100) - Number of results per page
  • email (string, optional) - Filter contacts by exact email address
  • phone_number (string, optional) - Filter contacts by phone number. The number will be normalized to E.164 format before matching.

Example Request:

curl -X GET "https://chatlane.io/api/v1/contacts" \
  -H "Authorization: Bearer {your-token}"

Example: Find Contact by Email

curl -X GET "https://chatlane.io/api/v1/[email protected]" \
  -H "Authorization: Bearer {your-token}"

Example: Find Contact by Phone Number

curl -X GET "https://chatlane.io/api/v1/contacts?phone_number=+441234567890" \
  -H "Authorization: Bearer {your-token}"

Both filters can be combined; results are always scoped to the authenticated token's team and returned as a paginated list (even if only a single contact matches).

Get Single Contact

GET /api/v1/contacts/{id}

Get detailed information about a specific contact.

Example Request:

curl -X GET "https://chatlane.io/api/v1/contacts/1" \
  -H "Authorization: Bearer {your-token}"

Create Contact

POST /api/v1/contacts

Create a new contact.

Request Body:

{
  "name": "John Doe",
  "email": "[email protected]",
  "phone_number": "+1234567890",
  "custom_attributes": {
    "company": "Acme Corp",
    "role": "Manager"
  }
}

Example Request:

curl -X POST "https://chatlane.io/api/v1/contacts" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Doe",
    "email": "[email protected]"
  }'

Update Contact

PUT/PATCH /api/v1/contacts/{id}

Update an existing contact. All fields are optional - only provided fields will be updated.

Request Body:

{
  "name": "Jane Doe",
  "email": "[email protected]",
  "phone_number": "+1987654321",
  "custom_attributes": {
    "company": "New Corp",
    "role": "Director",
    "department": "Engineering"
  }
}

Example Request:

curl -X PUT "https://chatlane.io/api/v1/contacts/1" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Doe",
    "email": "[email protected]",
    "custom_attributes": {
      "company": "New Corp",
      "role": "Director"
    }
  }'

Example Response:

{
  "data": {
    "id": 1,
    "name": "Jane Doe",
    "email": "[email protected]",
    "phone_number": "+1987654321",
    "custom_attributes": {
      "company": "New Corp",
      "role": "Director",
      "department": "Engineering"
    },
    "created_at": "2026-01-26T10:00:00Z",
    "updated_at": "2026-01-26T11:00:00Z"
  }
}

Note: If you update the email or phone number to a value that already exists for another contact in your team, you will receive a 422 error with code DUPLICATE_EMAIL or DUPLICATE_PHONE.

Automation: Identify & Track

These two endpoints power Chatlane Automation (segments, broadcasts, and workflows). They mirror the identify/track pattern used by tools like Customer.io, so existing integrations translate directly. See the Automation articles for how this data is used.

Identify Contact (Upsert + Attributes)

POST /api/v1/contacts/identify

Find a contact by id, email, or phone number — creating them if no match exists — and sync typed marketing attributes onto their profile. Attributes are the building blocks of segment rules (e.g. currency equals GBP, total_spend ≥ 1000).

Request Body:

{
  "identifiers": { "email": "[email protected]" },
  "name": "Jane Doe",
  "attributes": {
    "currency": "GBP",
    "locale": "en-GB",
    "total_orders": 4,
    "total_spend": 1240.50,
    "vip": true,
    "signed_up_at": "2026-01-15"
  }
}
  • identifiers (object, required) - One of id (Chatlane contact id), email, or phone_number. Resolution priority is id → email → phone.
  • name (string, optional) - Set or update the contact's name.
  • attributes (object, optional) - Flat map of scalar values. Keys may contain letters, digits, underscores, dashes and dots. Types are inferred from the first value seen for each key (number, boolean, date, or text) and drive which operators segments offer.

Responses:

  • 201 - Contact created (returns the contact resource)
  • 200 - Existing contact updated
  • 404 with code CONTACT_NOT_FOUND - An internal id was given that doesn't exist, and no email/phone was provided to create from
  • 422 - Non-scalar attribute values or unsafe attribute keys

Example Request:

curl -X POST "https://chatlane.io/api/v1/contacts/identify" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "identifiers": { "email": "[email protected]" },
    "attributes": { "currency": "GBP", "total_spend": 1240.50 }
  }'

Note: Identify writes to the contact's typed attribute store used by Automation. It never touches the custom_attributes JSON field used by the Create/Update Contact endpoints above.

Track Event

POST /api/v1/events

Record an event against a contact — e.g. request_quote, place_order, approve_order. Events power segment rules ("performed X at least N times in the last Y days"), trigger workflows, and attribute workflow conversions.

Request Body:

{
  "identifiers": { "email": "[email protected]" },
  "name": "place_order",
  "properties": {
    "order_total": 420.50,
    "service": "Certified Translation"
  },
  "timestamp": 1753400000,
  "id": "order-12345"
}
  • identifiers (object, required) - id, email, or phone_number of an existing contact
  • name (string, required) - Event name; use consistent snake_case (letters, digits, _, -, ., spaces)
  • properties (object, optional) - Event payload. Filterable in segment and workflow rules ("where property service equals Certified Translation"), and used for conversion values (e.g. order_total)
  • timestamp (integer, optional) - Unix timestamp of when the event occurred; defaults to now
  • id (string, optional) - Idempotency key. Re-sending the same key is a no-op, so you can safely retry

Responses:

  • 201 with {"event_id": 123} - Event recorded
  • 200 with {"duplicate": true} - Idempotency key already used; nothing recorded
  • 404 with code CONTACT_NOT_FOUND - No contact matches the identifiers. Track never auto-creates contacts — call identify first

Example Request:

curl -X POST "https://chatlane.io/api/v1/events" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "identifiers": { "email": "[email protected]" },
    "name": "request_quote",
    "properties": { "service": "Certified Translation", "quote_value": 250 },
    "id": "quote-9876"
  }'

Recommended integration order: call identify when a customer signs up or their profile changes, then track for each meaningful action. Segments re-evaluate automatically within seconds of each call.

Custom Entity Records

List Custom Entity Records

GET /api/v1/custom-entity-records

List custom entity records with optional filtering.

Query Parameters:

  • created_after (string, ISO 8601) - Filter records created after this timestamp (for polling)
  • custom_entity_id (integer) - Filter by custom entity ID
  • limit (integer, default: 50, max: 100) - Number of results per page

Example Request:

curl -X GET "https://chatlane.io/api/v1/custom-entity-records?custom_entity_id=1" \
  -H "Authorization: Bearer {your-token}"

Example Response:

{
  "data": [
    {
      "id": 1,
      "custom_entity_id": 1,
      "external_id": "EXT-12345",
      "values": {
        "name": "Order #12345",
        "status": "pending"
      },
      "custom_entity": {
        "id": 1,
        "name": "Orders",
        "slug": "orders"
      },
      "created_at": "2026-01-26T10:00:00Z",
      "updated_at": "2026-01-26T10:00:00Z"
    }
  ],
  "links": {...},
  "meta": {...}
}

Get Single Custom Entity Record

GET /api/v1/custom-entity-records/{id}

Get detailed information about a specific custom entity record.

Example Request:

curl -X GET "https://chatlane.io/api/v1/custom-entity-records/1" \
  -H "Authorization: Bearer {your-token}"

Example Response:

{
  "data": {
    "id": 1,
    "custom_entity_id": 1,
    "external_id": "EXT-12345",
    "values": {
      "name": "Order #12345",
      "status": "pending",
      "amount": 99.99
    },
    "custom_entity": {
      "id": 1,
      "name": "Orders",
      "slug": "orders",
      "attributes": [...]
    },
    "conversations": [...],
    "contacts": [...],
    "created_at": "2026-01-26T10:00:00Z",
    "updated_at": "2026-01-26T10:00:00Z"
  }
}

Create Custom Entity Record

POST /api/v1/custom-entity-records

Create a new custom entity record. The custom_entity_id is required to specify which custom entity type this record belongs to.

Request Body:

{
  "custom_entity_id": 1,
  "external_id": "EXT-12345",
  "values": {
    "name": "Order #12345",
    "status": "pending",
    "amount": 99.99
  }
}

Parameters:

  • custom_entity_id (required, integer) - The ID of the custom entity this record belongs to. Must exist and belong to your team.
  • external_id (optional, string) - An external identifier for the record. If not provided and the custom entity has an external_id attribute defined, it will be extracted from the values object.
  • values (required, object) - The record's attribute values. Must match the custom entity's attribute definitions.

Example Request:

curl -X POST "https://chatlane.io/api/v1/custom-entity-records" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "custom_entity_id": 1,
    "values": {
      "name": "Order #12345",
      "status": "pending"
    }
  }'

Example Response:

{
  "data": {
    "id": 1,
    "custom_entity_id": 1,
    "external_id": "Order #12345",
    "values": {
      "name": "Order #12345",
      "status": "pending"
    },
    "custom_entity": {
      "id": 1,
      "name": "Orders",
      "slug": "orders"
    },
    "created_at": "2026-01-26T10:00:00Z",
    "updated_at": "2026-01-26T10:00:00Z"
  }
}

Note: The custom_entity_id is always included in the response, even when the custom_entity relationship is not loaded. This allows you to identify which custom entity type the record belongs to without needing to load the full relationship.

Update Custom Entity Record

PUT/PATCH /api/v1/custom-entity-records/{id}

Update an existing custom entity record. You can update the values object and/or the external_id.

Request Body:

{
  "external_id": "EXT-12345-UPDATED",
  "values": {
    "name": "Order #12345",
    "status": "completed",
    "amount": 149.99
  }
}

Parameters:

  • external_id (optional, string) - Update the external identifier for the record.
  • values (optional, object) - Update the record's attribute values. If provided, must match the custom entity's attribute definitions. If the custom entity has an external_id attribute defined and you update that value in values, the external_id field will be automatically updated.

Example Request:

curl -X PUT "https://chatlane.io/api/v1/custom-entity-records/1" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "values": {
      "name": "Order #12345",
      "status": "completed",
      "amount": 149.99
    }
  }'

Example Response:

{
  "data": {
    "id": 1,
    "custom_entity_id": 1,
    "external_id": "EXT-12345",
    "values": {
      "name": "Order #12345",
      "status": "completed",
      "amount": 149.99
    },
    "custom_entity": {
      "id": 1,
      "name": "Orders",
      "slug": "orders"
    },
    "created_at": "2026-01-26T10:00:00Z",
    "updated_at": "2026-01-26T11:00:00Z"
  }
}

Note: When updating values, you can provide a partial update (only the fields you want to change) or a complete update (all fields). The custom_entity_id cannot be changed after creation.

POST /api/v1/custom-entity-records/{id}/contacts

Attach a contact to a custom entity record with a role (e.g. the people on an order). In a single request you can either reference an existing contact by contact_id, or pass contact details (email/name/phone_number) to find or create the contact, then attach it.

Request Body:

{
  "email": "[email protected]",
  "name": "Jamie Chen",
  "role": "client"
}

Parameters:

  • contact_id (optional, integer) - Attach an existing contact by id.
  • email (optional, string) - Find a contact by email, or create one if none exists.
  • name (optional, string) - Name used when creating a new contact.
  • phone_number (optional, string) - E.164 phone; used to find or create the contact.
  • custom_attributes (optional, object) - Stored on the contact when it is created.
  • role (optional, string) - The contact's role on this record (must match a role defined on the custom entity, e.g. client, vendor, project_manager, team_member).

You must provide at least one of contact_id, email, or name.

Behaviour:

  • Find-or-create: with no contact_id, the contact is looked up by email then phone_number; if none matches a new contact is created.
  • Idempotent: a contact can only be attached to a record once. Re-posting the same contact updates its role instead of creating a duplicate.
  • The record and contact must belong to the token's team.

Example Request:

curl -X POST "https://chatlane.io/api/v1/custom-entity-records/1/contacts" \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "name": "Jamie Chen",
    "role": "client"
  }'

Example Response (201):

{
  "message": "Contact attached successfully",
  "data": {
    "id": 10,
    "custom_entity_record_id": 1,
    "contact_id": 42,
    "team_id": 1,
    "role": "client",
    "contact": {
      "id": 42,
      "name": "Jamie Chen",
      "email": "[email protected]",
      "phone_number": null
    },
    "created_at": "2026-06-05T10:00:00Z",
    "updated_at": "2026-06-05T10:00:00Z"
  }
}

Inboxes (Read-Only)

List Inboxes

GET /api/v1/inboxes

List all inboxes for reference (read-only).

Example Request:

curl -X GET "https://chatlane.io/api/v1/inboxes" \
  -H "Authorization: Bearer {your-token}"

Get Single Inbox

GET /api/v1/inboxes/{id}

Get information about a specific inbox (read-only).

Example Request:

curl -X GET "https://chatlane.io/api/v1/inboxes/1" \
  -H "Authorization: Bearer {your-token}"

Zapier Integration

Setting Up Zapier

  1. Generate API Token:

    • Log in to Chatlane
    • Go to Settings → API Tokens
    • Create a new token for your Zapier integration
    • Copy the token value
  2. Configure Zapier:

    • In your Zapier app, add API Key authentication
    • Use the token as the API key
    • Set the Base URL to: https://chatlane.io/api/v1
  3. Create Triggers:

    • Use polling triggers with created_after parameter
    • Example: GET /api/v1/conversations?created_after={last_poll_time}
  4. Create Actions:

    • Use POST endpoints to create resources
    • Example: POST /api/v1/conversations/{id}/messages to send messages

Example Zapier Trigger (New Conversations)

Polling URL: https://chatlane.io/api/v1/conversations

Query Parameters:

  • created_after: Use the timestamp from your last poll
  • status: active (optional)

Sample Response:

{
  "data": [
    {
      "id": 1,
      "subject": "New customer inquiry",
      "status": "active",
      "participant": {
        "name": "John Doe",
        "email": "[email protected]"
      }
    }
  ]
}

Example Zapier Action (Send Message)

Action URL: https://chatlane.io/api/v1/conversations/{conversation_id}/messages

Method: POST

Headers:

Authorization: Bearer {your-token}
Content-Type: application/json

Body:

{
  "body": "{{message_body}}",
  "type": "email",
  "is_draft": false
}

Error Codes

Code Description
UNAUTHENTICATED No valid API token provided
UNAUTHORIZED Token is valid but user lacks permission
NOT_FOUND Resource not found
VALIDATION_ERROR Request validation failed
TEAM_ACCESS_DENIED Token is not valid for the requested team
INVALID_CHANNEL Specified channel is not active or does not match message type
NO_CHANNEL_FOUND No active channel of the specified type found
CONTACT_REQUIRED Contact must be specified for entity record conversations
CONTACT_NOT_ASSOCIATED Specified contact is neither linked to the entity record nor a participant in the conversation
NO_EMAIL_ADDRESS Contact does not have an email address
NOTE_CANNOT_BE_DRAFT Notes cannot be saved as drafts
MESSAGE_TOO_LONG Message exceeds maximum length (e.g., 1600 chars for WhatsApp)
DUPLICATE_EMAIL Contact with this email already exists
DUPLICATE_PHONE Contact with this phone number already exists

Best Practices

  1. Polling: Use created_after parameter with ISO 8601 timestamps for efficient polling
  2. Pagination: Always handle pagination using links and meta in responses
  3. Error Handling: Check HTTP status codes and error response structure
  4. Rate Limiting: Implement exponential backoff for rate limit errors
  5. Token Security: Store tokens securely and never expose them in client-side code
  6. Team Scoping: Remember that tokens are scoped to specific teams

Support

For API support, contact [email protected] or browse the help center.