# Mirlo API — Context for Claude

This file gives Claude the context it needs to work with the Mirlo API.
Drop it in your project root as `CLAUDE.md` and Claude Code will read it automatically.

## Authentication

All requests use the `X-API-Key` header. Keys start with `sk_live_`.

```
X-API-Key: sk_live_your_api_key
```

The key is scoped to one organization. Get your `organization_id` with:

```bash
curl https://api.mirlo.com/v1/organizations -H "X-API-Key: $MIRLO_API_KEY"
```

**Important:** Always use the `X-API-Key` header, NOT `Authorization: Bearer`. The gateway interprets any `Authorization: Bearer` value as a JWT and validates it — an API key sent that way always returns 401:

```json
{ "message": "Invalid JWT: invalid jwt string" }
```

## API Products

| Product | Base URL | Description |
|---------|----------|-------------|
| Messages | `https://api.mirlo.com/v2/messages/organizations/{org_id}` | WhatsApp, Instagram, Messenger |
| Voice | `https://api.mirlo.com/v2/voice/organizations/{org_id}` | AI voice agents, outbound campaigns |
| Meetings | `https://api.mirlo.com/v1/meetings` | Record, transcribe & analyze meetings with AI |
| Documents | `https://api.mirlo.com/v2/documents` | Message attachments (images, PDFs, audio) |
| Agents | `https://api.mirlo.com/v1/members/organizations/{org_id}` | AI agent configuration (prompt, tools, voice) |
| Members | `https://api.mirlo.com/v1/members/organizations/{org_id}` | Team members, availability, status |
| CRM | `https://api.mirlo.com/v1/crm/organizations/{org_id}` | Deals, tickets, contacts, accounts, products |
| Flows | `https://api.mirlo.com/v1/flows` | Visual automation flows |

---

## Messages

Send and receive messages on WhatsApp, Instagram, and Messenger.
Base: `https://api.mirlo.com/v2/messages/organizations/{org_id}`

### Check service window (24h)

WhatsApp only allows free-text messages within 24 hours of the contact's last message. Outside that window, you must use an approved template. This endpoint tells you if the window is open.

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/contacts/addresses/{phone}/service-window?organization_address={phone_number_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Returns `{"status": "open"}` or `{"status": "closed"}`. If open, send free text. If closed, send a template.

### Send template message

The simplest and recommended way to send a message. Uses a Meta-approved template.

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/messages/send-template" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_address": "100000000000001",
    "to": "+525512345678",
    "meta_template_id": "999888777666555",
    "do_not_pause": false,
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "parameter_name": "nombre_cliente", "text": "Juan Perez" }
        ]
      }
    ]
  }'
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `organization_address` | string | Yes | WhatsApp Phone Number ID |
| `to` | string | Yes | Recipient phone (E.164 format) |
| `meta_template_id` | string | Yes | Template ID from Meta |
| `do_not_pause` | boolean | No | If true, don't pause the bot after sending. Default: false |
| `components` | array | No | Template variables (header, body, buttons) |

Header with image example — add a `header` component with `type: "image"`:

```json
{
  "type": "header",
  "parameters": [
    { "type": "image", "image": { "link": "https://example.com/image.jpg" } }
  ]
}
```

### Send free-text message

Direct message without a template. Only works within the 24-hour service window.

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/messages" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_address": "100000000000001",
    "service": "whatsapp",
    "contact_address": "5215512345678",
    "direction": "outgoing",
    "content": {
      "kind": "text",
      "text": "Hello from Mirlo"
    }
  }'
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `organization_address` | string | Yes | WhatsApp Phone Number ID |
| `service` | string | Yes | `whatsapp`, `instagram`, `messenger` |
| `contact_address` | string | Yes | Recipient phone (no + prefix) |
| `direction` | string | Yes | Always `outgoing` |
| `content.kind` | string | Yes | `text`, `image`, `video`, `audio`, `document` |
| `content.text` | string | Depends | Message text (required for text, caption for media) |
| `content.file` | object | For media | `{ "uri": "https://...", "mime_type": "image/jpeg" }` |

### List messages

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/messages?conversation_id={id}&limit=50" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Filters: `conversation_id`, `direction` (incoming/outgoing), `start_date`, `end_date`.

### Get single message

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/messages/{message_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Message statuses: `pending` → `accepted` → `sent` → `delivered` → `read`. Check `status.error` on `failed`.

### Message endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `.../messages/send-template` | Send template message (recommended) |
| POST | `.../messages` | Send free-text message (24h window) |
| GET | `.../messages` | List messages |
| GET | `.../messages/{id}` | Get single message |
| GET | `.../contacts/addresses/{phone}/service-window` | Check 24h service window |

---

## Templates

Manage WhatsApp message templates (approved by Meta).
Base: `https://api.mirlo.com/v2/messages/organizations/{org_id}/whatsapp-management`

### List templates

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/whatsapp-management/templates?organization_address={phone_number_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Pagination uses Meta's cursor-based system: `limit`, `after`, `before` (not `offset`).

### Create template

Template fields go nested inside a `template` key, with `organization_address` at root level. This is the most common mistake.

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/whatsapp-management/templates" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_address": "100000000000001",
    "template": {
      "name": "my_template",
      "category": "MARKETING",
      "language": "es",
      "parameter_format": "NAMED",
      "components": [
        {
          "type": "BODY",
          "text": "Hello {{customer_name}}, your order #{{order_id}} is ready.",
          "example": {
            "body_text_named_params": [
              { "param_name": "customer_name", "example": "Juan" },
              { "param_name": "order_id", "example": "12345" }
            ]
          }
        }
      ]
    }
  }'
```

Categories: `MARKETING` (promos, opt-in required), `UTILITY` (transactional), `AUTHENTICATION` (OTP/verification).

### Edit template

Only templates with status `APPROVED` or `REJECTED` can be edited. Edited templates go back to `PENDING`.

```bash
curl -X PATCH "https://api.mirlo.com/v2/messages/organizations/{org_id}/whatsapp-management/templates" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_address": "100000000000001",
    "template": {
      "id": "999888777666555",
      "components": [...]
    }
  }'
```

### Delete template

Identify by **name in the body** (not by ID in the URL). `DELETE .../templates/{id}` returns 404.

```bash
curl -X DELETE "https://api.mirlo.com/v2/messages/organizations/{org_id}/whatsapp-management/templates" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_address": "100000000000001",
    "template": { "name": "template_to_delete" }
  }'
```

Template statuses: `APPROVED`, `PENDING`, `REJECTED`, `PAUSED`.

### Template endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/templates` | List templates |
| POST | `/templates` | Create template |
| PATCH | `/templates` | Edit template |
| DELETE | `/templates` | Delete template (by name in body) |
| GET | `/marketing-usage` | Marketing template usage |

---

## Conversations

Conversations group all messages between your organization and a contact.
Base: `https://api.mirlo.com/v2/messages/organizations/{org_id}`

### List conversations

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations?status=active&limit=20" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Key filters: `status` (active/closed/archived), `search`, `contact_id`, `service` (whatsapp,messenger), `labels` (comma-separated), `assigned_member_id`, `since`/`until`, `broadcast_id`, `is_broadcast`, `include_messages` (include last N messages, max 50).

### Get conversation

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{conversation_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Update conversation

Update labels, assign agents, archive/unarchive. Fields in `extra` are merged (not replaced).

```bash
curl -X PATCH "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{conversation_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "extra": {
      "member_id": "agent-uuid",
      "labels": ["vip", "support"]
    }
  }'
```

Updatable `extra` fields: `member_id` (assign/unassign agent — null to unassign), `labels` (string array), `archived` (ISO timestamp to archive, null to unarchive), `team_id`, `team_name`.

### Archive conversation (recommended)

Archives and clears assignment, team, labels. Ready for a fresh start if contact writes again.

```bash
curl -X PATCH "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "extra": {
      "archived": "2026-01-22T18:30:00.000Z",
      "member_id": null,
      "assignment": null,
      "team_id": null,
      "team_name": null,
      "labels": []
    }
  }'
```

Archived conversations unarchive automatically when the contact sends a new message.

### Assign to team

Transfers conversation to a team. The system finds an available member and assigns automatically.

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{id}/assign-team" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "team_id": "team-uuid", "team_name": "Support L2" }'
```

### Notes

Internal comments not visible to the contact.

```bash
# Create note
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{id}/notes" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Follow up next week", "organization_id": "{org_id}" }'

# List notes
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{id}/notes" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Delete note
curl -X DELETE "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{id}/notes/{note_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Events (audit trail)

Assignment history, label changes, archiving events.

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/{id}/events?types=assignment" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Get addresses by label

Get all phone numbers from conversations with a specific label. Useful for building broadcast recipient lists.

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/conversations/addresses-by-label?label=vip&organization_id={org_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Conversation endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `.../conversations` | List conversations |
| GET | `.../conversations/{id}` | Get conversation |
| PATCH | `.../conversations/{id}` | Update (extra, status, name) |
| DELETE | `.../conversations/{id}` | Delete conversation |
| POST | `.../conversations/{id}/assign-team` | Transfer to team |
| POST | `.../conversations/{id}/notes` | Create note |
| GET | `.../conversations/{id}/notes` | List notes |
| DELETE | `.../conversations/{id}/notes/{note_id}` | Delete note |
| GET | `.../conversations/{id}/events` | Audit trail |
| GET | `.../conversations/{id}/labels/enriched` | Labels with sequences |
| GET | `.../conversations/addresses-by-label` | Phone numbers by label |

---

## Contacts (Messaging)

Contacts represent people your organization has interacted with via messaging channels.
Base: `https://api.mirlo.com/v2/messages/organizations/{org_id}`

```bash
# List contacts (supports search by name/address, limit/offset pagination)
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/contacts?search=juan&limit=20" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Get single contact
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/contacts/{contact_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

---

## Members (Messaging)

Team members who handle conversations. Can be humans or AI agents.
Base: `https://api.mirlo.com/v2/messages/organizations/{org_id}`

### List members

```bash
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/members" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Each member has `ai: true` (AI agent) or `ai: false` (human).

### Create member

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/members" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Carlos Lopez",
    "ai": false,
    "organization_id": "{org_id}"
  }'
```

### Update member

```bash
curl -X PATCH "https://api.mirlo.com/v2/messages/organizations/{org_id}/members/{member_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Carlos Lopez Updated" }'
```

### Delete member

Deleting a member does NOT reassign their active conversations. Reassign or archive first.

```bash
curl -X DELETE "https://api.mirlo.com/v2/messages/organizations/{org_id}/members/{member_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `.../members` | List members |
| GET | `.../members/{id}` | Get member |
| POST | `.../members` | Create member |
| PATCH | `.../members/{id}` | Update member |
| DELETE | `.../members/{id}` | Delete member |

---

## Labels & Access Control

Base: `https://api.mirlo.com/v2/messages/organizations/{org_id}`

### Organization labels

Labels are a dictionary at the org level. Add labels to the org, then apply them to conversations.

```bash
# Add labels to org dictionary
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/labels" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "labels": [{ "name": "vip" }, { "name": "support" }] }'

# Delete label (cascades to all conversations)
curl -X DELETE "https://api.mirlo.com/v2/messages/organizations/{org_id}/labels/vip" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Access control

Restrict which channels (WhatsApp numbers, Instagram accounts) each member can see.

```bash
# Get config
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/access-control" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Update config (modes: by_address, by_assignment, both)
curl -X PUT "https://api.mirlo.com/v2/messages/organizations/{org_id}/access-control" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "mode": "by_address" }'

# Grant member access to a channel
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/addresses/{address}/members" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "member_id": "agent-uuid" }'

# Revoke access
curl -X DELETE "https://api.mirlo.com/v2/messages/organizations/{org_id}/addresses/{address}/members/{member_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Admins and super admins always see all conversations regardless of access control.

---

## Broadcasts

Mass message sending via WhatsApp templates.
Base: `https://api.mirlo.com/v2/messages/organizations/{org_id}`

### Flow: Create → Send → Monitor

1. Get templates: `GET .../whatsapp-management/templates`
2. Create broadcast: `POST .../broadcasts`
3. Send: `POST .../broadcasts/{id}/send`
4. Monitor: `GET .../broadcasts/{id}` and `GET .../broadcasts/{id}/recipients`

### Create broadcast

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/broadcasts" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "January Campaign",
    "organization_address": "100000000000001",
    "meta_template_id": "111222333444555",
    "recipients": [
      { "phone_number": "+521234567890" },
      {
        "phone_number": "+521234567891",
        "components": [
          {
            "type": "body",
            "parameters": [
              { "type": "text", "parameter_name": "nombre", "text": "Maria" }
            ]
          }
        ]
      }
    ]
  }'
```

Each recipient can have their own `components` for personalized variables. Max 1000 recipients per request — make multiple requests for more.

Created in `draft` status. Save the returned `id` to send in the next step.

### Send broadcast

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/broadcasts/{id}/send" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Add more recipients (draft only)

```bash
curl -X POST "https://api.mirlo.com/v2/messages/organizations/{org_id}/broadcasts/{id}/recipients" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "recipients": [{ "phone_number": "+521234567893" }] }'
```

### Monitor progress

```bash
# Broadcast status: draft → pending → sending → completed/failed
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/broadcasts/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Per-recipient status: pending → sent → delivered → read / failed / replied
curl "https://api.mirlo.com/v2/messages/organizations/{org_id}/broadcasts/{id}/recipients" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

List filters: `status`, `created_by`, `include_metrics` (adds delivery/response metrics).

`assigned_member_id` assigns resulting conversations to a human member (AI agents return 400).

### Broadcast endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `.../broadcasts` | Create broadcast |
| GET | `.../broadcasts` | List broadcasts |
| GET | `.../broadcasts/{id}` | Get broadcast |
| GET | `.../broadcasts/{id}/full` | Broadcast with recipients and template |
| PATCH | `.../broadcasts/{id}` | Update (draft only) |
| DELETE | `.../broadcasts/{id}` | Delete (draft only) |
| POST | `.../broadcasts/{id}/recipients` | Add recipients |
| GET | `.../broadcasts/{id}/recipients` | List recipients |
| POST | `.../broadcasts/{id}/send` | Send broadcast |

---

## Voice

AI-powered voice calls and outbound campaigns.
Base: `https://api.mirlo.com/v2/voice/organizations/{org_id}`

### Initiate AI call

Start an outbound call handled by an AI agent.

```bash
curl -X POST "https://api.mirlo.com/v2/voice/organizations/{org_id}/calls/outbound-ai" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+521234567890",
    "agent_id": "agent-uuid"
  }'
```

### List calls

```bash
curl "https://api.mirlo.com/v2/voice/organizations/{org_id}/calls" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Get call detail

```bash
curl "https://api.mirlo.com/v2/voice/organizations/{org_id}/calls/{call_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Active calls

```bash
curl "https://api.mirlo.com/v2/voice/organizations/{org_id}/calls/active" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Outbound campaigns

Create mass calling campaigns with imported contact lists.

```bash
# Create campaign
curl -X POST "https://api.mirlo.com/v2/voice/organizations/{org_id}/outbound-queues" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Collections Jan 2026", "agent_id": "agent-uuid" }'

# Import contacts
curl -X POST "https://api.mirlo.com/v2/voice/organizations/{org_id}/outbound-queues/{id}/items/import" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "items": [{ "phone_number": "+521234567890" }] }'

# Get campaign stats
curl "https://api.mirlo.com/v2/voice/organizations/{org_id}/outbound-queues/{id}/stats" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Recordings & Transcriptions

```bash
# Get recording URL (presigned)
curl "https://api.mirlo.com/v2/voice/organizations/{org_id}/recordings/call/{call_id}/url" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Get transcription
curl "https://api.mirlo.com/v2/voice/organizations/{org_id}/transcriptions/call/{call_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Call rejection report

```bash
curl "https://api.mirlo.com/v2/voice/organizations/{org_id}/stats/rejections" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Voice endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `.../calls/outbound-ai` | Initiate AI call |
| GET | `.../calls` | List calls |
| GET | `.../calls/{id}` | Call detail |
| GET | `.../calls/active` | Active calls |
| POST | `.../outbound-queues` | Create campaign |
| GET | `.../outbound-queues` | List campaigns |
| POST | `.../outbound-queues/{id}/items/import` | Import contacts |
| GET | `.../outbound-queues/{id}/stats` | Campaign stats |
| GET | `.../recordings/call/{id}/url` | Recording URL |
| GET | `.../transcriptions/call/{id}` | Transcription |
| GET | `.../stats/rejections` | Rejection report |

---

## Meetings

Record, transcribe, and analyze meetings from Google Meet, Zoom, and Microsoft Teams with an AI bot. Also supports voice notes (in-person meeting recordings from the browser).

Meetings API requires both `X-API-Key` and `x-organization-id` headers:

```bash
curl "https://api.mirlo.com/v1/meetings" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### List meetings

```bash
# Supports: userId, teamId, status, platform, search, dateFrom, dateTo, participantName
# Sort: newest (default), oldest, longest
curl "https://api.mirlo.com/v1/meetings?page=1&limit=20&platform=google_meet&sort=newest" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### Get meeting detail

Returns full meeting info including AI-generated summary, action items, participants, and duration.

```bash
curl "https://api.mirlo.com/v1/meetings/{meeting_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### Update meeting

```bash
curl -X PATCH "https://api.mirlo.com/v1/meetings/{meeting_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated title"}'
```

### Delete meeting

```bash
curl -X DELETE "https://api.mirlo.com/v1/meetings/{meeting_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### Get transcript

Returns paginated transcript segments with speaker name, text, and audio offset in seconds.

```bash
curl "https://api.mirlo.com/v1/meetings/{meeting_id}/transcript?page=1&limit=100" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### Search across transcripts

```bash
curl "https://api.mirlo.com/v1/meetings/transcripts/search?q=pricing&page=1&limit=20" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### Get recording URL

Returns a presigned URL to download the audio/video recording.

```bash
curl "https://api.mirlo.com/v1/meetings/{meeting_id}/recording" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### Action items

Toggle completion of an AI-generated action item.

```bash
curl -X PATCH "https://api.mirlo.com/v1/meetings/{meeting_id}/action-items/{item_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{"completed": true}'
```

### Bookmarks

Create a bookmark at a specific moment in the recording.

```bash
# Create bookmark
curl -X POST "https://api.mirlo.com/v1/meetings/{meeting_id}/bookmarks" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{"offset_seconds": 125, "note": "Key decision about pricing"}'

# Delete bookmark
curl -X DELETE "https://api.mirlo.com/v1/meetings/{meeting_id}/bookmarks/{bookmark_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID"
```

### Voice notes

Record in-person meetings from the browser. Upload audio (max 100MB) and optionally analyze with AI.

```bash
curl -X POST "https://api.mirlo.com/v1/meetings/voice-notes" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "x-organization-id: $MIRLO_ORG_ID" \
  -F "audio=@recording.webm" \
  -F "title=Client meeting" \
  -F "analyze=true" \
  -F "template=bant"
```

Analysis templates: `general`, `qa`, `one_on_one`, `sales_discovery`, `bant`, `meddpicc`, `spiced`, `demo`, `customer_success`, `interview`.

### Calendar connections

Connect Google or Microsoft calendar for automatic meeting recording.

```bash
curl -X POST "https://api.mirlo.com/v1/meetings/calendar/connections" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organizationId": "{org_id}",
    "userId": "{user_id}",
    "mcpApiKey": "mcp_key_...",
    "email": "user@company.com"
  }'
```

### Organization settings

```bash
# Get settings
curl "https://api.mirlo.com/v1/meetings/settings/org/{org_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Update (botName, autoRecord, defaultSummaryTemplate, joinDelaySeconds, etc.)
curl -X PATCH "https://api.mirlo.com/v1/meetings/settings/org/{org_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"botName": "My Notetaker", "autoRecord": true, "joinDelaySeconds": 30}'
```

### Meetings endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/meetings` | List meetings |
| GET | `/meetings/{id}` | Meeting detail |
| PATCH | `/meetings/{id}` | Update meeting |
| DELETE | `/meetings/{id}` | Delete meeting |
| PATCH | `/meetings/{id}/action-items/{itemId}` | Toggle action item |
| POST | `/meetings/{id}/bookmarks` | Create bookmark |
| DELETE | `/meetings/{id}/bookmarks/{bookmarkId}` | Delete bookmark |
| GET | `/meetings/{id}/recording` | Recording URL |
| GET | `/meetings/{id}/transcript` | Paginated transcript |
| GET | `/meetings/transcripts/search` | Search transcripts |
| POST | `/meetings/voice-notes` | Upload voice note |
| GET | `/settings/org/{orgId}` | Org settings |
| PUT | `/settings/org/{orgId}` | Update org settings |
| GET | `/settings/user` | User settings |
| PUT | `/settings/user` | Update user settings |
| POST | `/calendar/connections` | Connect calendar |
| GET | `/calendar/connections` | List connected calendars |
| PATCH | `/calendar/connections/{id}` | Update connection |
| DELETE | `/calendar/connections/{id}` | Disconnect calendar |

---

## Documents (Media Downloads)

Download message attachments (images, PDFs, audio, video). Files are stored securely and scoped per organization.
Base: `https://api.mirlo.com/v2/documents`

### How it works

Messages with file attachments have `content.file.uri` starting with `internal://`. Strip that prefix to get the download key.

```json
{
  "content": {
    "kind": "image",
    "file": {
      "uri": "internal://organizations/{org_id}/attachments/abc123",
      "name": "photo.jpg",
      "mime_type": "image/jpeg"
    }
  }
}
```

### Download a file

```bash
# URI: internal://organizations/{org_id}/attachments/abc123
# Key: organizations/{org_id}/attachments/abc123

curl -X POST "https://api.mirlo.com/v2/documents/objects/download" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key": "organizations/{org_id}/attachments/abc123"}' \
  --output downloaded_file.jpg
```

Returns the binary file with the appropriate `Content-Type`.

### List files

```bash
curl "https://api.mirlo.com/v2/documents/objects" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Get file metadata

```bash
# By ID
curl "https://api.mirlo.com/v2/documents/objects/{object_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# By key
curl "https://api.mirlo.com/v2/documents/objects/by-key?key=organizations/{org_id}/attachments/abc123" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### End-to-end example

```bash
# 1. Get messages
MESSAGES=$(curl -s "https://api.mirlo.com/v2/messages/organizations/{org_id}/messages?conversation_id={id}" \
  -H "X-API-Key: $MIRLO_API_KEY")

# 2. Extract file URI
FILE_URI=$(echo $MESSAGES | jq -r '.data[] | select(.content.file) | .content.file.uri' | head -1)

# 3. Strip "internal://" prefix
KEY=$(echo $FILE_URI | sed 's|^internal://||')

# 4. Download
curl -X POST "https://api.mirlo.com/v2/documents/objects/download" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"key\": \"$KEY\"}" \
  --output file.jpg
```

Each API key can only access files from its own organization. Cross-org access returns 404.

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/objects` | List files |
| GET | `/objects/{id}` | Metadata by ID |
| GET | `/objects/by-key` | Metadata by key |
| POST | `/objects/download` | Download file |

---

## AI Agents

Configure AI agents that handle WhatsApp, webchat, and voice conversations autonomously.
Base: `https://api.mirlo.com/v1/members/organizations/{org_id}`

### List AI agents

```bash
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/members/ai-agents?limit=20" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Filters: `search` (by name), `category` (sales, support, etc.), `sort` (name, created_at), `order` (asc/desc), `page`, `limit`.

### Get agent detail

```bash
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/members/ai-agents/{agent_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Get agent config

Returns the full prompt sections, tools, voice settings, and transfer rules.

```bash
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/members/{agent_id}/agent-config" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Create/replace config (POST)

Replaces the entire configuration.

```bash
curl -X POST "https://api.mirlo.com/v1/members/organizations/{org_id}/members/{agent_id}/agent-config" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sections": {
      "identity": "You are a sales assistant for Mirlo...",
      "rules": "Always greet the contact by name...",
      "knowledge": "Basic Plan: $199/mo, Pro Plan: $499/mo..."
    },
    "tools": {
      "transfer_to_human": { "enabled": true },
      "search_knowledge": { "enabled": true },
      "send_product": { "enabled": true }
    },
    "voice": "female",
    "agent_type": "generic",
    "business_category": "sales"
  }'
```

### Update config partially (PATCH)

Only the provided fields are updated. Other sections are preserved.

```bash
curl -X PATCH "https://api.mirlo.com/v1/members/organizations/{org_id}/members/{agent_id}/agent-config" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sections": {
      "knowledge": "Updated FAQ: How to reset password..."
    }
  }'
```

### Prompt sections

Sections are flexible key-value text blocks. Common ones:

| Section | Purpose |
|---------|---------|
| `identity` | Name, role, personality |
| `rules` | Behavioral rules, restrictions |
| `knowledge` | Business info, products, FAQ |
| `tone` | Communication style |
| `closing` | How to close conversations |
| `escalation` | When/how to transfer to human |

### Chat behavior settings

Set via `sections.chat`:

| Field | Range | Default | Description |
|-------|-------|---------|-------------|
| `temperature` | 0-1 | 0.7 | Creativity level |
| `context_messages` | 0-100 | 50 | How many previous messages as context |
| `context_days` | 1-30 | 7 | Only messages from last N days |
| `split_long_messages` | bool | false | Split long responses into multiple WhatsApp messages |

### Available tools

| Tool | Description |
|------|-------------|
| `transfer_to_human` | Transfer conversation to a human agent |
| `search_knowledge` | Search the organization's knowledge base |
| `send_product` | Send a product from the catalog |
| `create_ticket` | Create a support ticket |
| `schedule_meeting` | Schedule a meeting |

### Tool notifications

```bash
curl -X PATCH "https://api.mirlo.com/v1/members/organizations/{org_id}/members/{agent_id}/agent-config" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notifications": {
      "end_of_call": {
        "email": ["supervisor@company.com"],
        "whatsapp": ["+521234567890"],
        "webhook": "https://your-server.com/webhook/call-ended"
      }
    }
  }'
```

### Agent endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `.../members/ai-agents` | List AI agents |
| GET | `.../members/ai-agents/{id}` | Agent detail |
| GET | `.../members/{id}/agent-config` | Get config |
| POST | `.../members/{id}/agent-config` | Create/replace config |
| PATCH | `.../members/{id}/agent-config` | Update config partially |

---

## Member Availability

Manage presence status for team members. Members can be online, offline, busy, or away.
Base: `https://api.mirlo.com/v1/members/organizations/{org_id}`

### Get all member availability

```bash
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/members/availabilities" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Change member status

```bash
curl -X PATCH "https://api.mirlo.com/v1/members/organizations/{org_id}/members/{member_id}/status" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "busy" }'
```

Statuses: `online` (available), `offline`, `busy` (no new assignments), `away` (temporarily absent).

### Unavailable reasons

Custom reasons for going offline/busy (e.g., "Lunch", "Training").

```bash
# List
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/members/unavailable-reasons" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Create
curl -X POST "https://api.mirlo.com/v1/members/organizations/{org_id}/members/unavailable-reasons" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Lunch", "description": "Lunch break" }'

# Update
curl -X PATCH "https://api.mirlo.com/v1/members/organizations/{org_id}/members/unavailable-reasons/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Meal break" }'

# Delete
curl -X DELETE "https://api.mirlo.com/v1/members/organizations/{org_id}/members/unavailable-reasons/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Status history

```bash
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/members/status-history/organization" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Availability stats & exports

```bash
# Stats
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/stats/member-availability" \
  -H "X-API-Key: $MIRLO_API_KEY"

curl "https://api.mirlo.com/v1/members/organizations/{org_id}/stats/member-status" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Exports
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/stats/export/availability" \
  -H "X-API-Key: $MIRLO_API_KEY"

curl "https://api.mirlo.com/v1/members/organizations/{org_id}/stats/export/status-events" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Team availability
curl "https://api.mirlo.com/v1/members/organizations/{org_id}/teams/{team_id}/availability" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Availability endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `.../members/availabilities` | All member availability |
| PATCH | `.../members/{id}/status` | Change status |
| GET | `.../members/unavailable-reasons` | List reasons |
| POST | `.../members/unavailable-reasons` | Create reason |
| PATCH | `.../members/unavailable-reasons/{id}` | Update reason |
| DELETE | `.../members/unavailable-reasons/{id}` | Delete reason |
| GET | `.../members/status-history/organization` | Status history |
| GET | `.../stats/member-availability` | Availability stats |
| GET | `.../stats/member-status` | Status stats |
| GET | `.../stats/export/availability` | Export availability |
| GET | `.../stats/export/status-events` | Export status events |
| GET | `.../teams/{id}/availability` | Team availability |

---

## CRM

Manage sales opportunities, support tickets, contacts, accounts, and products.
Base: `https://api.mirlo.com/v1/crm/organizations/{org_id}`

### Deals (Opportunities)

#### Create deal

```bash
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Enterprise Plan Acme Corp",
    "pipeline_id": "pipeline-uuid",
    "stage_id": "stage-uuid",
    "amount": 85000,
    "currency": "MXN",
    "owner_id": "member-uuid",
    "expected_close_date": "2026-08-15"
  }'
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Deal name |
| `pipeline_id` | string | Yes | Pipeline UUID |
| `stage_id` | string | Yes | Initial stage UUID |
| `amount` | number | No | Estimated amount |
| `currency` | string | No | ISO 4217, default MXN. Supported: MXN, USD, EUR, COP, ARS, BRL, CLP, PEN, UYU, BOB, PYG, CRC, DOP, GTQ, HNL, NIO, PAB, GBP, CAD |
| `status` | string | No | `open` (default), `won`, `lost` |
| `owner_id` | string | No | Responsible member UUID |
| `account_id` | string | No | Linked account |
| `expected_close_date` | string | No | YYYY-MM-DD |
| `conversation_ids` | string[] | No | Conversations to link |
| `contacts` | array | No | `[{ contact_id, role?, is_primary? }]` |

#### List deals

```bash
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals?status=open&pipeline_id={id}&sort_by=amount&sort_dir=desc&limit=20" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Key filters: `pipeline_id`, `stage_id`, `owner_id`, `status`, `account_id`, `product_id`, `amount_min`/`amount_max`, `currency`, `unassigned`, `expected_close_before`/`after`, `created_before`/`after`, `search`, `sort_by` (amount/created_at/updated_at/name/expected_close_date), `timezone`.

#### Move deal stage

Moving to a stage with `won`/`lost` status auto-updates the deal.

```bash
curl -X PATCH "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/{id}/stage" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stage_id": "new-stage-uuid" }'
```

#### Update deal

```bash
curl -X PATCH "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 95000, "status": "won" }'
```

#### Deal line items (products)

Products in a deal recalculate the deal amount automatically.

```bash
# Add product to deal
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/{id}/line-items" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Enterprise Plan",
    "product_id": "prod-uuid",
    "quantity": 10,
    "unit_price": 8500,
    "discount": 0
  }'

# Delete line item
curl -X DELETE "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/{id}/line-items/{item_id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

#### Link conversation to deal

Captures the AI summary of the conversation automatically.

```bash
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/{id}/conversations" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "conversation_id": "conv-uuid", "channel": "whatsapp" }'
```

#### Stage time tracking

How long a deal spent in each pipeline stage.

```bash
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/{id}/stage-times" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Pipelines

```bash
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/pipelines" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Tickets

#### Create ticket

```bash
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Billing error",
    "source": "api",
    "priority": "high",
    "requester_id": "contact-uuid"
  }'
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | Yes | Ticket title |
| `source` | string | Yes | `manual`, `api`, `automation`, `form`, `channel` |
| `priority` | string | No | `urgent`, `high`, `normal` (default), `low` |
| `body_html` | string | No | Description in HTML |
| `requester_id` | string | No | Contact UUID |
| `assignee_member_id` | string | No | Assigned member UUID |
| `account_id` | string | No | Linked account |
| `channel` | string | No | `whatsapp`, `voice`, `email`, `sms`, `webchat`, `messenger` |
| `conversation_ids` | string[] | No | Conversations to link |

#### List tickets

```bash
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets?state=new&priority=high&limit=20" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Filters: `state`, `priority`, `assignee_member_id`, `unassigned`, `requester_id`, `account_id`, `search`, `created_after`/`before`, `timezone`.

#### Change ticket state

States: `new` → `waiting_on_agent` → `waiting_on_customer` → `on_hold` → `closed`.

```bash
curl -X PATCH "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets/{id}/state" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "state": "closed" }'
```

#### Assign ticket

```bash
curl -X PATCH "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets/{id}/assign" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "assignee_member_id": "member-uuid" }'
```

#### Search by reference

```bash
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets/by-ref/SOT-1042" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### CRM Contacts

Different from messaging contacts. Support identities (phone, email, etc.) and link to accounts.

```bash
# Create
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/contacts" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Juan",
    "last_name": "Perez",
    "identities": [
      { "channel": "whatsapp", "address": "+525512345678" },
      { "channel": "email", "address": "juan@company.com" }
    ],
    "properties": { "company": "Acme Corp", "city": "CDMX" }
  }'

# List (search by name, phone, or email)
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/contacts?search=juan&limit=10" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Update
curl -X PATCH "https://api.mirlo.com/v1/crm/organizations/{org_id}/contacts/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "properties": { "company": "Acme Corp Updated" } }'

# Delete (soft delete)
curl -X DELETE "https://api.mirlo.com/v1/crm/organizations/{org_id}/contacts/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Identity channels: `whatsapp`, `phone`, `email`, `sms`, `webchat`, `messenger`.

### Accounts

Companies/organizations linked to contacts and deals.

```bash
# Create
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/accounts" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corporation",
    "type": "enterprise",
    "domain": "acme.com",
    "properties": { "industry": "Tech", "employees": 150 }
  }'

# List
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/accounts" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Delete (soft delete)
curl -X DELETE "https://api.mirlo.com/v1/crm/organizations/{org_id}/accounts/{id}" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Products

Product catalog with media, costs, and organization into catalogs.

```bash
# Create product
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/products" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Enterprise Plan",
    "sku": "PLAN-ENT-001",
    "category": "plans",
    "price_amount": 4999,
    "price_currency": "MXN",
    "status": "active"
  }'

# List products (search, status filter)
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/products?search=plan&status=active" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Create catalog
curl -X POST "https://api.mirlo.com/v1/crm/organizations/{org_id}/products/catalogs" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Main Catalog", "is_default": true, "status": "active" }'

# Link products to catalog
curl -X PUT "https://api.mirlo.com/v1/crm/organizations/{org_id}/products/catalogs/{catalog_id}/products" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "product_ids": ["prod-1", "prod-2"] }'

# Internal cost (separate from price, not exposed in catalogs)
curl -X PUT "https://api.mirlo.com/v1/crm/organizations/{org_id}/products/{id}/cost" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "cost_amount": 2500, "cost_currency": "MXN" }'
```

### Analytics

#### Deals analytics

All under `.../deals/analytics`. Require `pipeline_id`.

```bash
# KPIs and breakdowns
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/analytics?pipeline_id={id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Timeline (created/won/lost over time)
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/analytics/timeline?pipeline_id={id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Conversion funnel between stages
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/analytics/funnel?pipeline_id={id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Weighted forecast by expected close month
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/analytics/forecast?pipeline_id={id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Stale deals ranked by days since last activity
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/analytics/aging?pipeline_id={id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Kanban summary
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/kanban-summary?pipeline_id={id}" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Export CSV
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals/export?status=open" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

#### Tickets analytics

```bash
# KPIs with breakdowns by status, priority, project, type, channel, assignee. Includes SLA metrics.
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets/analytics" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Timeline
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets/analytics/timeline" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Aging (open tickets by age buckets)
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/tickets/analytics/aging" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

#### AI Insights (per contact)

```bash
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/insights?contact_id={id}&insight_type=sentiment" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Types: `sentiment`, `intent`, `risk`, `churn`, `topic`.

#### Product mentions

AI-detected product mentions in conversations.

```bash
# List mentions (filter by product_id, catalog_id, contact_id, channel, date range)
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/product-mentions" \
  -H "X-API-Key: $MIRLO_API_KEY"

# Summary, trends, top products, unmatched products
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/product-mentions/summary" \
  -H "X-API-Key: $MIRLO_API_KEY"

curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/product-mentions/trends?granularity=week" \
  -H "X-API-Key: $MIRLO_API_KEY"

curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/product-mentions/top-products" \
  -H "X-API-Key: $MIRLO_API_KEY"

curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/product-mentions/unmatched" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### CRM endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| **Deals** | | |
| POST | `.../deals` | Create deal |
| GET | `.../deals` | List deals |
| GET | `.../deals/{id}` | Get deal |
| PATCH | `.../deals/{id}` | Update deal |
| PATCH | `.../deals/{id}/stage` | Move stage |
| DELETE | `.../deals/{id}` | Delete deal |
| POST | `.../deals/{id}/line-items` | Add product |
| PATCH | `.../deals/{id}/line-items/{itemId}` | Update product |
| DELETE | `.../deals/{id}/line-items/{itemId}` | Remove product |
| POST | `.../deals/{id}/conversations` | Link conversation |
| DELETE | `.../deals/{id}/conversations/{linkId}` | Unlink conversation |
| GET | `.../deals/{id}/stage-times` | Time per stage |
| GET | `.../pipelines` | List pipelines |
| **Tickets** | | |
| POST | `.../tickets` | Create ticket |
| GET | `.../tickets` | List tickets |
| GET | `.../tickets/{id}` | Get ticket |
| GET | `.../tickets/by-ref/{ref}` | Find by reference |
| PATCH | `.../tickets/{id}` | Update ticket |
| PATCH | `.../tickets/{id}/assign` | Assign ticket |
| PATCH | `.../tickets/{id}/state` | Change state |
| **Contacts** | | |
| POST | `.../contacts` | Create contact |
| GET | `.../contacts` | List contacts |
| GET | `.../contacts/{id}` | Get contact |
| PATCH | `.../contacts/{id}` | Update contact |
| DELETE | `.../contacts/{id}` | Delete contact |
| **Accounts** | | |
| POST | `.../accounts` | Create account |
| GET | `.../accounts` | List accounts |
| GET | `.../accounts/{id}` | Get account |
| PATCH | `.../accounts/{id}` | Update account |
| DELETE | `.../accounts/{id}` | Delete account |
| **Products** | | |
| POST | `.../products` | Create product |
| GET | `.../products` | List products |
| GET | `.../products/{id}` | Get product |
| PATCH | `.../products/{id}` | Update product |
| DELETE | `.../products/{id}` | Delete product |
| PUT | `.../products/replace` | Replace all products |
| POST | `.../products/catalogs` | Create catalog |
| GET | `.../products/catalogs` | List catalogs |
| PATCH | `.../products/catalogs/{id}` | Update catalog |
| DELETE | `.../products/catalogs/{id}` | Delete catalog |
| GET | `.../products/catalogs/{id}/products` | Catalog products |
| PUT | `.../products/catalogs/{id}/products` | Replace catalog products |
| GET | `.../products/{id}/cost` | Get internal cost |
| PUT | `.../products/{id}/cost` | Set internal cost |
| DELETE | `.../products/{id}/cost` | Remove internal cost |
| **Analytics** | | |
| GET | `.../deals/analytics` | Deal KPIs |
| GET | `.../deals/analytics/timeline` | Deal timeline |
| GET | `.../deals/analytics/funnel` | Conversion funnel |
| GET | `.../deals/analytics/forecast` | Forecast |
| GET | `.../deals/analytics/aging` | Stale deals |
| GET | `.../deals/kanban-summary` | Kanban summary |
| GET | `.../deals/export` | CSV export |
| GET | `.../tickets/analytics` | Ticket KPIs |
| GET | `.../tickets/analytics/timeline` | Ticket timeline |
| GET | `.../tickets/analytics/aging` | Ticket aging |
| GET | `.../insights` | AI insights |
| GET | `.../product-mentions` | Product mentions |
| GET | `.../product-mentions/summary` | Mentions summary |
| GET | `.../product-mentions/trends` | Mention trends |
| GET | `.../product-mentions/top-products` | Top products |
| GET | `.../product-mentions/unmatched` | Unmatched mentions |

---

## Flows

Visual automation flows — create, validate, publish, and operate programmatically.
Base: `https://api.mirlo.com/v1/flows`

The organization is derived from the API key — routes don't include `organization_id`.

### List flows

```bash
curl "https://api.mirlo.com/v1/flows" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

### Create flow

```bash
curl -X POST "https://api.mirlo.com/v1/flows" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome Flow",
    "description": "Greet new contacts",
    "canvas": { ... }
  }'
```

### Get, update, delete

```bash
curl "https://api.mirlo.com/v1/flows/{flow_id}" -H "X-API-Key: $MIRLO_API_KEY"

curl -X PATCH "https://api.mirlo.com/v1/flows/{flow_id}" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Updated name", "canvas": { ... } }'

curl -X DELETE "https://api.mirlo.com/v1/flows/{flow_id}" -H "X-API-Key: $MIRLO_API_KEY"
```

### Lifecycle: publish, pause, archive

```bash
curl -X POST "https://api.mirlo.com/v1/flows/{id}/publish" -H "X-API-Key: $MIRLO_API_KEY"
curl -X POST "https://api.mirlo.com/v1/flows/{id}/pause" -H "X-API-Key: $MIRLO_API_KEY"
curl -X POST "https://api.mirlo.com/v1/flows/{id}/archive" -H "X-API-Key: $MIRLO_API_KEY"
```

### Duplicate

```bash
curl -X POST "https://api.mirlo.com/v1/flows/{id}/duplicate" -H "X-API-Key: $MIRLO_API_KEY"
```

### Test with sample data

```bash
curl -X POST "https://api.mirlo.com/v1/flows/{id}/test" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "trigger_data": { ... } }'
```

### Versions (revisions)

```bash
# List versions
curl "https://api.mirlo.com/v1/flows/{id}/revisions" -H "X-API-Key: $MIRLO_API_KEY"

# Get specific version
curl "https://api.mirlo.com/v1/flows/{id}/revisions/{version}" -H "X-API-Key: $MIRLO_API_KEY"

# Rollback to version
curl -X POST "https://api.mirlo.com/v1/flows/{id}/rollback" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "version": 3 }'

# Unpublished changes
curl "https://api.mirlo.com/v1/flows/{id}/unpublished-changes" -H "X-API-Key: $MIRLO_API_KEY"
```

### Validate

```bash
# Validate saved flow
curl -X POST "https://api.mirlo.com/v1/flows/{id}/validate" -H "X-API-Key: $MIRLO_API_KEY"

# Validate canvas without saving
curl -X POST "https://api.mirlo.com/v1/flows/validate-canvas" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "canvas": { ... } }'
```

### Executions

```bash
# List executions
curl "https://api.mirlo.com/v1/flows/{id}/executions" -H "X-API-Key: $MIRLO_API_KEY"

# Execution detail
curl "https://api.mirlo.com/v1/flows/{id}/executions/{executionId}" -H "X-API-Key: $MIRLO_API_KEY"
```

### Import/Export

```bash
# Export
curl "https://api.mirlo.com/v1/flows/{id}/export" -H "X-API-Key: $MIRLO_API_KEY"

# Import
curl -X POST "https://api.mirlo.com/v1/flows/import" \
  -H "X-API-Key: $MIRLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
```

### Node catalog

```bash
curl "https://api.mirlo.com/v1/flows/node-catalog" -H "X-API-Key: $MIRLO_API_KEY"
```

### Permissions

Each endpoint requires a permission on the API key: `flows:view`, `flows:create`, `flows:update`, `flows:delete`, `flows:publish`, `flows:pause`. Missing permission returns 403.

### Flows endpoints reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/flows` | List flows |
| POST | `/flows` | Create flow |
| GET | `/flows/{id}` | Get flow |
| PATCH | `/flows/{id}` | Update flow |
| DELETE | `/flows/{id}` | Delete flow |
| POST | `/flows/{id}/publish` | Publish |
| POST | `/flows/{id}/pause` | Pause |
| POST | `/flows/{id}/archive` | Archive |
| POST | `/flows/{id}/duplicate` | Duplicate |
| POST | `/flows/{id}/test` | Test with sample data |
| GET | `/flows/{id}/revisions` | List versions |
| GET | `/flows/{id}/revisions/{v}` | Get version |
| POST | `/flows/{id}/rollback` | Rollback to version |
| GET | `/flows/{id}/unpublished-changes` | Check pending changes |
| POST | `/flows/{id}/validate` | Validate saved flow |
| POST | `/flows/validate-canvas` | Validate canvas |
| GET | `/flows/node-catalog` | Available nodes |
| GET | `/flows/{id}/export` | Export |
| POST | `/flows/import` | Import |
| GET | `/flows/{id}/executions` | Execution history |
| GET | `/flows/{id}/executions/{execId}` | Execution detail |
| GET | `/flows/by-agent/{agentId}` | Flows using an agent |
| GET | `/flows/phone-numbers/mappings` | Phone numbers linked to flows |
| * | `/flows/folders` | Folder CRUD |

---

## Webhooks

Real-time event notifications for your organization.

### Messaging events

| Event | Description |
|-------|-------------|
| `message.received` | New incoming message |
| `message.status` | Message status change (sent, delivered, read, failed) |
| `conversation.created` | New conversation |
| `conversation.updated` | Conversation state change |

### Voice events (coming soon)

| Event | Description |
|-------|-------------|
| `call.initiated` | Call started |
| `call.answered` | Call answered |
| `call.completed` | Call completed |
| `call.failed` | Call failed |
| `call.transcription` | Transcription available |

---

## Pagination

All list endpoints support `limit` and `offset` query parameters (except template listing which uses cursor-based pagination from Meta).

```bash
curl "https://api.mirlo.com/v1/crm/organizations/{org_id}/deals?limit=20&offset=40" \
  -H "X-API-Key: $MIRLO_API_KEY"
```

Response format:

```json
{
  "data": [...],
  "total": 347,
  "limit": 20,
  "offset": 40
}
```

`skip`/`take` are aliases for `offset`/`limit` kept for backward compatibility. If both are sent, `limit`/`offset` wins.

## Common Patterns

- Replace `{org_id}` with your organization UUID
- Replace `$MIRLO_API_KEY` with your actual API key (or set it as env var)
- All timestamps are ISO 8601
- Responses use `{"data": [...]}` envelope for lists
- Error responses include `statusCode`, `error`, and `message`

## Full Documentation

https://developers.mirlo.com/docs
