Skip to content
AISARAISAR
REST API

Funnels

Sales funnels and their stages — the Kanban structure deals move through. Through the API you list funnels, create and update a funnel together with its stages in a single request, delete funnels, and fetch the Kanban board with deals grouped by stage.

Funnel fields

A funnel carries name (up to 255 chars), description (up to 2000 chars, nullable) and status (draft, active, paused or archived).

Stages

Stages are created and updated together with the funnel in a single request; each stage carries name, type (new, in_progress, success or failed), color (up to 16 chars) and order.

Funnel board

The funnel board (/board) returns up to 50 deal cards per stage together with the stage aggregates (deals_count, total_amount); use the stage-level paginated endpoint to load more cards in a single column (perPage defaults to 50).

Since 2026-09-26 the board (GET /funnels/{funnel}/board) needs the deal.view permission, like the stage-level paginated endpoint (without it — 403). The deal card stays visible to everyone, but the last-message quote (last_message_preview) goes empty when that message sits on a channel channel_access rules close to the user; last_message_at and the channel icon remain.

A deal card on the board and in the stage-level paginated endpoint (GET /funnels/{funnel}/stages/{stage}/deals) additionally carries Kanban signals: contact_id, channel_id (the channel of the last conversation), responsible_user_id, awaiting_reply_since (an ISO time since which the customer has been waiting for a reply, or null), next_task — the nearest open task {id, type, title, due_at, is_overdue} or null — and active_ai_agent — {id, name} of the AI agent enabled in the conversation, or null. Note: on the board awaiting_reply_since is computed from the last 30 messages of the conversation and only for the 200 freshest cards per request; the rest get null even if the customer is waiting.

A deal card (the board, the stage-level paginated endpoint and the GET /funnels/{funnel}/deals list) carries funnel_stage_id — the id of the stage the deal sits in; the list, which has no board columns, uses it to tell where the deal is.

Funnel deal list

GET /funnels/{funnel}/deals is a server-side list of the deals of one funnel (cards in the same shape as on the board). All parameters are optional: page (≥1), perPage — strictly 25, 50 or 100, sort — updated_at (default), amount, expected_close_date, next_task_due_at, created_at or title, dir — asc/desc, stage_id (a stage of this same funnel, otherwise 422), responsible_user_id[] (up to 50 ids; the value 0 means "unassigned"), status[] (open, won, lost), stale (boolean: only stale or only fresh deals, by the stage stale_after_days rule), search (up to 100 characters: matches the deal title and the contact name). Response: {"data": {"items": [...], "pagination": {"page", "perPage", "total", "lastPage"}, "has_stage_automations": boolean}}; has_stage_automations tells whether the company has active automations on deal events (a client warns about them before a bulk action). Requires deal.view; an embed token gets 403; a foreign or missing funnel is 404; the limit is 120 requests per minute per user (plus the shared heavy-request limit for Bearer tokens), beyond it — 429.

Funnel conversion

GET /funnels/{funnel}/conversion is a summary for the last 30 days (window_days) used by the board column headers and the funnel strip of the deals list. Permission: deal.view; an embed (iframe) token gets 403; another company's funnel answers 404; limit 60 requests per minute per user, the result is cached for 5 minutes (computed_at is the calculation time). The sample is the funnel's deals created or moved between stages inside the window (total_deals); while there are fewer than min_deals (20), show_conversion is false and every percentage (conversion_to_next, funnel_conversion) is null. Stage fields: stage_id, deals_count, total_amount_by_currency (a "currency → amount" object), conversion_to_next (a 0..1 share: of the deals that visited the stage, how many reached a later one; null for terminal stages). won is {count, amount_by_currency} of deals won in the window; funnel_conversion is the win rate among deals closed in the window, won / (won + lost), 0..1, or null.

"Unsorted"

Since 2026-09-29 every funnel has a virtual first column, "Unsorted": auto-leads created in auto_lead_mode: unsorted that nobody has accepted yet (details in the "Deals" group). Such a lead formally sits on the funnel's first stage, but it belongs to no board column, is not counted in the stages' deals_count/total_amount, and is not returned by stage paging. The board returns only a counter, the unsorted: {count} key next to funnel; the cards themselves come from GET /funnels/{funnel}/unsorted.

GET /funnels/{funnel}/unsorted needs deal.view (agents see the column too; managers and administrators sort it); another company's funnel answers 404. It takes page and perPage (5–50, default 20) and returns data.items + data.pagination, newest leads first (by unsorted_at). Each item is a board card (the same fields and the same channel_access rule for hiding last_message_preview) plus contact_id, source, unsorted_at, lead_conversation_id, contact_has_closed_deals (the client already has closed deals — the "Returning" badge), contact_phone (the contact's primary phone; null when the caller lacks contact.view) and last_inbound_missed_call (the card's last message is an incoming call nobody answered). Limited to 60 requests per minute per user.

Endpoints

MethodPath
GET/v1/funnels

List company funnels

POST/v1/funnels

Create a funnel (with stages)

POST/v1/funnels/{funnel}

Update a funnel

DELETE/v1/funnels/{funnel}

Delete a funnel

GET/v1/funnels/{funnel}/board

Kanban board of the funnel with deals grouped by stage

GET/v1/funnels/{funnel}/conversion

Funnel conversion over 30 days: deals and amounts per stage, conversion to the next stage, won deals and the overall win rate

GET/v1/funnels/{funnel}/stages/{stage}/deals

Paginated deals for a single stage

GET/v1/funnels/{funnel}/deals

Server-side deal list of a funnel ("List" view): filters stage_id, responsible_user_id[], status[], stale, search, sorting sort/dir, paging page/perPage (25|50|100)

GET/v1/funnels/{funnel}/unsorted

The funnel's "Unsorted" — a paginated list of auto-leads not accepted yet (perPage 5–50)

Examples

Funnel deal list with filters

Request

bash
curl -X GET "https://api.aisar.app/v1/funnels/3/deals?perPage=25&sort=amount&dir=desc&status[]=open&responsible_user_id[]=0&responsible_user_id[]=5" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Accept: application/json"

Response

json
{
  "data": {
    "items": [
      {
        "id": 501,
        "title": "Ремонт кухни",
        "status": "open",
        "amount": "450000.00",
        "currency": "KZT",
        "funnel_stage_id": 12,
        "contact_id": 123,
        "responsible_user_id": 5,
        "...": "..."
      }
    ],
    "pagination": { "page": 1, "perPage": 25, "total": 1, "lastPage": 1 },
    "has_stage_automations": false
  }
}

Funnel conversion over 30 days

Request

bash
curl -X GET https://api.aisar.app/v1/funnels/3/conversion \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Accept: application/json"

Response

json
{
  "data": {
    "funnel_id": 3,
    "window_days": 30,
    "min_deals": 20,
    "total_deals": 46,
    "show_conversion": true,
    "stages": [
      { "stage_id": 11, "deals_count": 14, "total_amount_by_currency": { "KZT": 2800000 }, "conversion_to_next": 0.62 },
      { "stage_id": 12, "deals_count": 9, "total_amount_by_currency": { "KZT": 1950000 }, "conversion_to_next": 0.41 },
      { "stage_id": 13, "deals_count": 6, "total_amount_by_currency": { "KZT": 1200000 }, "conversion_to_next": null }
    ],
    "won": { "count": 6, "amount_by_currency": { "KZT": 1200000 } },
    "funnel_conversion": 0.4,
    "computed_at": "2026-10-08T07:30:00+00:00"
  }
}

Create a funnel with stages

Request

bash
curl -X POST https://api.aisar.app/v1/funnels \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sales",
    "description": "Primary sales pipeline",
    "status": "active",
    "stages": [
      { "name": "Lead", "type": "new", "color": "#3B82F6", "order": 1 },
      { "name": "Negotiation", "type": "in_progress", "color": "#F59E0B", "order": 2 },
      { "name": "Won", "type": "success", "color": "#22C55E", "order": 3 }
    ]
  }'

Response

json
{
  "data": {
    "message": "Funnel created.",
    "funnel": {
      "id": 1,
      "company_id": 1,
      "name": "Sales",
      "slug": "sales",
      "description": "Primary sales pipeline",
      "steps": 3,
      "updated_at": "2026-03-15T10:00:00.000000Z",
      "stages": [
        { "id": 1, "funnel_id": 1, "name": "Lead", "type": "new", "order": 1, "color": "#3B82F6", "stale_after_days": null, "wip_limit": null },
        { "id": 2, "funnel_id": 1, "name": "Negotiation", "type": "in_progress", "order": 2, "color": "#F59E0B", "stale_after_days": null, "wip_limit": null },
        { "id": 3, "funnel_id": 1, "name": "Won", "type": "success", "order": 3, "color": "#22C55E", "stale_after_days": null, "wip_limit": null }
      ]
    }
  }
}

Funnel Kanban board

Request

bash
curl https://api.aisar.app/v1/funnels/1/board \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "data": {
    "funnel": { "id": 1, "name": "Sales" },
    "unsorted": { "count": 3 },
    "stages": [
      {
        "id": 1,
        "name": "Lead",
        "type": "new",
        "order": 1,
        "color": "#3B82F6",
        "stale_after_days": null,
        "wip_limit": null,
        "deals_count": 1,
        "total_amount": "5000.00",
        "deals": [
          {
            "id": 501,
            "title": "Office renovation",
            "amount": "5000.00",
            "currency": "KZT",
            "status": "open",
            "expected_close_date": null,
            "stage_changed_at": "2026-03-15T10:00:00.000000Z",
            "age_days": 3,
            "is_stale": false,
            "channel": "whatsapp",
            "contact_name": "Aigerim Bekova",
            "contact_avatar": null,
            "responsible_user_name": "Aidos",
            "last_message_preview": "Hello, can we schedule a call?",
            "last_message_at": "2026-03-15T09:40:00.000000Z",
            "conversations_count": 2,
            "contact_id": 301,
            "channel_id": 12,
            "responsible_user_id": 5,
            "awaiting_reply_since": "2026-03-15T09:40:00.000000Z",
            "next_task": { "id": 77, "type": "call", "title": "Call back", "due_at": "2026-03-16T10:00:00.000000Z", "is_overdue": false },
            "active_ai_agent": null
          }
        ]
      }
    ]
  }
}

Page through a stage column

Request

bash
curl "https://api.aisar.app/v1/funnels/1/stages/1/deals?page=2&perPage=50" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "data": {
    "items": [
      {
        "id": 542,
        "title": "Warehouse lease",
        "amount": "12000.00",
        "currency": "KZT",
        "status": "open",
        "expected_close_date": "2026-04-01",
        "stage_changed_at": "2026-03-14T08:00:00.000000Z",
        "age_days": 4,
        "is_stale": false,
        "channel": "telegram",
        "contact_name": "Marat Ospanov",
        "contact_avatar": null,
        "responsible_user_name": "Aidos",
        "last_message_preview": null,
        "last_message_at": null,
        "conversations_count": 1
      }
    ],
    "pagination": { "page": 2, "perPage": 50, "total": 120, "lastPage": 3 }
  }
}

The funnel's "Unsorted"

Request

bash
curl "https://api.aisar.app/v1/funnels/1/unsorted?perPage=20" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "data": {
    "items": [
      {
        "id": 640,
        "title": "Dana S.",
        "amount": null,
        "currency": "KZT",
        "status": "open",
        "expected_close_date": null,
        "stage_changed_at": "2026-09-29T15:40:00.000000Z",
        "age_days": 0,
        "is_stale": false,
        "channel": "whatsapp",
        "contact_name": "Dana S.",
        "contact_avatar": null,
        "responsible_user_name": null,
        "last_message_preview": "Здравствуйте, сколько стоит доставка?",
        "last_message_at": "2026-09-29T15:40:00.000000Z",
        "conversations_count": 1,
        "contact_id": 318,
        "source": "whatsapp",
        "unsorted_at": "2026-09-29T15:40:00.000000Z",
        "lead_conversation_id": 9120,
        "contact_has_closed_deals": false,
        "contact_phone": "+12025550143",
        "last_inbound_missed_call": false
      }
    ],
    "pagination": { "page": 1, "perPage": 20, "total": 3, "lastPage": 1 }
  }
}