inDeal API: deals and reports

Lars Krüger

Lars Krüger

Last updated on Sep 26, 2026

With the inDeal API you create deals for your leads, move them through the stages to closing and fetch ready-made figures without paging through lists yourself. Key, base URL and basics are the same as for leads: inDeal API: create and update leads via the interface.

Contents

What a deal is

A deal comes from a lead and keeps its origin in lead_id. It moves through the stages send_offer and order_received and ends in won or lost. Each lead has at most one open deal - the same rule as in the app, see From lead to deal and back.

Create a deal

POST /deals creates a deal. lead_id is required; the lead has to exist in your account, otherwise the API responds with 404.

curl -X POST https://app.indeal.ai/api/v1/deals \
  -H "Authorization: Bearer indeal_..." \
  -H "Content-Type: application/json" \
  -d '{
    "lead_id": "0b0e...",
    "value": 12000,
    "confidence": 40,
    "expected_close_date": "2026-10-15"
  }'

Response with status 201:

{
  "status": "created",
  "deal": {
    "id": "7c1d...",
    "lead_id": "0b0e...",
    "stage": "send_offer",
    "value": 12000,
    "confidence": 40,
    "weighted_value": 4800,
    "expected_close_date": "2026-10-15",
    "won_at": null,
    "lost_at": null,
    "url": "https://app.indeal.ai/pipeline/7c1d...",
    "lead": {
      "id": "0b0e...",
      "full_name": "Julia Weber",
      "company_name": "Muster GmbH",
      "email": "julia.weber@muster.de",
      "url": "https://app.indeal.ai/leads/0b0e..."
    }
  }
}

Three things happen automatically:

  • If value is missing, the deal takes the lead's estimated value (est_value). If confidence is missing, it is 25.
  • The lead now counts as converted. Its stage stays unchanged.
  • If the lead already has a deal that is not in the trash, no second one is created. You get status 200 with "status": "duplicate" and the existing deal. So simply check status instead of looking it up first.

Read and update a deal

GET /deals/{id} returns { "deal": { ... } }. An unknown id or a deal from another account results in 404.

PATCH /deals/{id} changes only the fields you send. null empties a field.

curl -X PATCH https://app.indeal.ai/api/v1/deals/7c1d... \
  -H "Authorization: Bearer indeal_..." \
  -H "Content-Type: application/json" \
  -d '{ "stage": "won", "value": 15000 }'
  • stage switches the stage. won sets won_at, lost sets lost_at to now. inDeal writes the history of stage changes itself.
  • With lost, lost_reason is worth adding - the reason shows in the app and in the webhook event deal.lost.
  • If you change next_step_due, a reminder set in the app for that date is dropped.

List deals

GET /deals returns deals page by page, sorted by last change.

Parameter Meaning
stage only this stage
lead_id only deals of this lead
updated_since ISO 8601, only deals changed since then
order asc (default, oldest first) or desc
limit 1 to 200, default 100
cursor next_cursor from the previous response

Response: { "deals": [...], "next_cursor": "..." }. If next_cursor is null, that was the last page. For totals and counts you do not need this list - that is what Reports are for.

Activities on a deal

Deals have their own timeline. POST /deals/{id}/activities and GET /deals/{id}/activities work exactly like activities on a lead: the same types (note, call, email, meeting_scheduled), the same fields, the same paging. An entry on a deal belongs to the deal only, not additionally to the lead.

Fields

Field Type Note
id, created_at, updated_at, stage_entered_at read-only
lead_id id originating lead, required on create
stage choice send_offer, order_received, won, lost
value number deal volume
confidence integer 0 to 100 probability in percent
weighted_value read-only value times confidence divided by 100
expected_close_date date YYYY-MM-DD expected closing
offer_sent_at, next_step_due ISO 8601
next_step, notes, lost_reason text lost_reason only on update
won_at, lost_at read-only set when switching to won or lost
url read-only link to the deal page in inDeal
lead read-only id, full_name, company_name, email and url of the lead
custom_data object custom fields for deals, same format and checks as for leads, see Custom fields; GET /custom-fields?entity=deal lists the fields

Not reachable via the API: the assignment to a team member, reminders and the trash.

Reports

GET /reports/summary returns ready-made figures for a period - leads, meetings, deals and conversion in one response. The figures cover the whole account, regardless of who a lead is assigned to. All times are UTC.

Parameter Meaning
from start, YYYY-MM-DD or ISO 8601. Default: the 1st of the current month
to end, YYYY-MM-DD (that day counts) or ISO 8601 (exclusive). Default: now
sections comma-separated from leads, meetings, deals, conversion. Default: all four

The period may be at most 366 days long and from has to be before to - otherwise the API responds with 400.

curl "https://app.indeal.ai/api/v1/reports/summary?from=2026-09-01&to=2026-09-30&sections=deals,conversion" \
  -H "Authorization: Bearer indeal_..."

Response with status 200, here with all four blocks:

{
  "period": { "from": "2026-09-01T00:00:00+00:00", "to": "2026-10-01T00:00:00+00:00" },
  "leads": {
    "created": 12,
    "by_stage": [ { "stage": "qualifying", "count": 40, "est_value_sum": 380000 } ],
    "by_source_tool": [ { "source_tool": "api", "count": 12 } ],
    "by_lead_source": [ { "lead_source": "Webinar", "count": 7 } ]
  },
  "meetings": { "scheduled": 5, "done": 3 },
  "deals": {
    "created": 3,
    "by_stage": [ { "stage": "send_offer", "count": 4, "value_sum": 54000, "weighted_sum": 21600 } ],
    "pipeline_value": 54000,
    "pipeline_weighted": 21600,
    "won_count": 1,
    "won_value": 15000,
    "lost_count": 1,
    "avg_days_to_won": 12.5
  },
  "conversion": {
    "leads_created": 12,
    "meetings_scheduled": 5,
    "deals_created": 3,
    "deals_won": 1,
    "meeting_rate": 0.4167,
    "deal_rate": 0.6,
    "win_rate": 0.3333
  }
}

Rates are numbers between 0 and 1 with four decimals. Leads and deals in the trash never count.

The figures in detail

The response holds two kinds of numbers: counters for the period (what happened between from and to) and snapshot figures of today (what the pipeline looks like right now). by_stage, pipeline_value and pipeline_weighted are snapshot figures, everything else refers to the period.

Figure Meaning
period.from, period.to the evaluated period, from inclusive, to exclusive
leads.created leads created in the period
leads.by_stage all current leads per stage with count and sum of the estimated value
leads.by_source_tool leads created in the period per source (for example api, import, extension)
leads.by_lead_source leads created in the period per origin from the lead source field, the 20 most frequent; empty means unknown
meetings.scheduled leads moved to meeting scheduled in the period
meetings.done leads moved to meeting done in the period
deals.created deals created in the period
deals.by_stage all current deals per stage with count, sum of values and weighted sum
deals.pipeline_value sum of the values of all open deals (send offer, order received), as of today
deals.pipeline_weighted the same sum, weighted by confidence
deals.won_count, deals.won_value deals won in the period, count and sum of values
deals.lost_count deals lost in the period
deals.avg_days_to_won average days from creating the deal to winning it, only for deals won in the period; null without won deals
conversion.leads_created same as leads.created
conversion.meetings_scheduled same as meetings.scheduled
conversion.deals_created same as deals.created
conversion.deals_won same as deals.won_count
conversion.meeting_rate meetings scheduled divided by leads created; null without leads
conversion.deal_rate deals created divided by meetings scheduled; null without meetings
conversion.win_rate deals won divided by deals created; null without deals

The rates in conversion use the counters of the same period. They are therefore not a true funnel of one cohort: a meeting in September can belong to a lead from August.

Next