{"openapi":"3.1.0","info":{"title":"Marro API","version":"1.0.0","description":"Server-to-server access to one organization’s data in Marro. Each key belongs to an organization and can use only the scopes it was given for modules the organization subscribes to. Keys go only in the Authorization header, over HTTPS, from a server: never in browser code or a URL. A key that was ever sent over plain HTTP or in a URL must be treated as leaked: revoke it and create a new one. The API sends no CORS headers.\n\nEvery response carries `X-Request-Id`, to quote to support. Once the key is checked, responses also carry `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds until the window resets) for whichever limit is closer to running out. An address that is not an endpoint answers 404 `NOT_FOUND` in the same error shape."},"servers":[{"url":"https://app.marro.si"}],"tags":[{"name":"Leads"},{"name":"Reference","description":"How this organization is set up: fields, stages, sources and people."}],"paths":{"/api/v1/leads":{"get":{"operationId":"list-leads","tags":["Leads"],"summary":"List leads","description":"Leads in the organization, newest first, a page at a time. Filter by stage, source, owner, time of last change, or any field. Archived leads are left out.\n\nScope: `leads.read`.","security":[{"ApiKey":["leads.read"]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many leads to return, 1–100.","schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"The `nextCursor` from the previous page.","schema":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647}},{"name":"updated_since","in":"query","required":false,"description":"Only leads changed at or after this time, for syncing.","schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},{"name":"stage_id","in":"query","required":false,"description":"Only leads in this stage (IDs from GET /api/v1/lead-stages).","schema":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647}},{"name":"pipeline_id","in":"query","required":false,"description":"Only leads in this pipeline (IDs from GET /api/v1/pipelines).","schema":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647}},{"name":"source_id","in":"query","required":false,"description":"Only leads from this source (IDs from GET /api/v1/lead-sources).","schema":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647}},{"name":"owner_id","in":"query","required":false,"description":"Only leads owned by this person (IDs from GET /api/v1/users).","schema":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647}},{"name":"filter","in":"query","required":false,"description":"A JSON array of field filters: `[{\"field\":\"city\",\"operator\":\"contains\",\"value\":\"Hyd\"}]`. All filters must match. The operators each field type takes are in this parameter’s `x-filter-operators`.","schema":{"type":"string","maxLength":8000},"x-filter-operators":{"maxFilters":10,"byType":[{"types":["text","textarea","phone","email","url"],"operators":[{"id":"contains","label":"contains","takes":"`value`"},{"id":"equals","label":"is","takes":"`value`"},{"id":"is_empty","label":"is empty","takes":"No value"},{"id":"is_not_empty","label":"is not empty","takes":"No value"}]},{"types":["number"],"operators":[{"id":"equals","label":"is","takes":"`value`"},{"id":"gt","label":"is more than","takes":"`value`"},{"id":"lt","label":"is less than","takes":"`value`"},{"id":"between","label":"is between","takes":"`values` with two items"},{"id":"is_empty","label":"is empty","takes":"No value"},{"id":"is_not_empty","label":"is not empty","takes":"No value"}]},{"types":["date"],"operators":[{"id":"on","label":"is on","takes":"`value`"},{"id":"before","label":"is before","takes":"`value`"},{"id":"after","label":"is after","takes":"`value`"},{"id":"between","label":"is between","takes":"`values` with two items"},{"id":"is_empty","label":"is empty","takes":"No value"},{"id":"is_not_empty","label":"is not empty","takes":"No value"}]},{"types":["select","lead_source","user"],"operators":[{"id":"is_any_of","label":"is any of","takes":"`values` with one or more items"},{"id":"is_empty","label":"is empty","takes":"No value"},{"id":"is_not_empty","label":"is not empty","takes":"No value"}]},{"types":["multiselect"],"operators":[{"id":"has_any_of","label":"includes any of","takes":"`values` with one or more items"},{"id":"is_empty","label":"is empty","takes":"No value"},{"id":"is_not_empty","label":"is not empty","takes":"No value"}]},{"types":["checkbox"],"operators":[{"id":"is_true","label":"is yes","takes":"No value"},{"id":"is_false","label":"is no","takes":"No value"}]}]}}],"responses":{"200":{"description":"A page of leads.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}},"nextCursor":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},{"type":"null"}],"description":"Pass as `cursor` for the next page; null on the last page."}},"required":["items","nextCursor"],"additionalProperties":false},"example":{"items":[{"id":1842,"name":"Priya Sharma","phone":"+919812345678","email":"priya@example.com","leadType":"student","stageId":7,"pipelineId":2,"sourceId":3,"assignedTo":21,"stage":{"id":7,"name":"Follow Up","meaning":"open"},"pipeline":{"id":2,"name":"Main pipeline"},"source":{"id":3,"name":"Website"},"owner":{"id":21,"name":"Ravi Kumar"},"fields":{"city":"Hyderabad","interest":"Premium plan","preferred_time":"evening"},"createdAt":"2026-09-20T04:45:00Z","updatedAt":"2026-09-24T03:32:11Z"}],"nextCursor":1841}}}},"400":{"description":"INVALID_QUERY: A parameter is out of range, `filter` is not valid JSON, or a filter cannot apply (unknown field key, an operator the field does not take, a bad value, more than 10). `details.filters` lists the positions of those filters (0-based); nothing is listed rather than ignoring them.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"create-lead","tags":["Leads"],"summary":"Create a lead","description":"Creates a lead with the organization’s own field rules: required fields, option lists and formats are checked exactly as in the app. The lead starts in the starting stage unless you give one, and without an owner the organization’s assignment rules pick one. Phone number and email are unique per organization, so a retry of the same lead returns DUPLICATE_LEAD with the existing ID instead of creating a second.\n\nScope: `leads.write`.","security":[{"ApiKey":["leads.write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}},{"type":"null"}]},"description":"Values by field key. `name` and `phone` are always required, plus any field the organization requires on create. `source` takes a source ID and `assigned_to` a user ID; without an owner the organization’s assignment rules decide."},"stageId":{"description":"Defaults to the starting stage of the lead’s pipeline. A stage names its own pipeline.","type":"integer","exclusiveMinimum":0,"maximum":2147483647},"pipelineId":{"description":"Defaults to the organization’s default pipeline. With a stageId, the stage must be one of this pipeline’s.","type":"integer","exclusiveMinimum":0,"maximum":2147483647}},"required":["fields"],"additionalProperties":false},"example":{"fields":{"name":"Priya Sharma","phone":"+919812345678","city":"Hyderabad","source":3,"preferred_time":"evening"}}}}},"responses":{"201":{"description":"The new lead.","content":{"application/json":{"schema":{"type":"object","properties":{"lead":{"$ref":"#/components/schemas/Lead"}},"required":["lead"],"additionalProperties":false},"example":{"lead":{"id":1842,"name":"Priya Sharma","phone":"+919812345678","email":"priya@example.com","leadType":"student","stageId":7,"pipelineId":2,"sourceId":3,"assignedTo":21,"stage":{"id":7,"name":"Follow Up","meaning":"open"},"pipeline":{"id":2,"name":"Main pipeline"},"source":{"id":3,"name":"Website"},"owner":{"id":21,"name":"Ravi Kumar"},"fields":{"city":"Hyderabad","interest":"Premium plan","preferred_time":"evening"},"createdAt":"2026-09-20T04:45:00Z","updatedAt":"2026-09-24T03:32:11Z"}}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"DUPLICATE_LEAD: A lead with this phone number or email already exists; `details.leadId` is its ID when known.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"VALIDATION_FAILED: A field is missing or invalid; `details.fields` says which. A refused stage, pipeline, owner or source gives `details.reason` (for example `STAGE_NOT_IN_PIPELINE`) and `details.field`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/leads/capture":{"post":{"operationId":"capture-lead","tags":["Leads"],"summary":"Capture a lead","description":"For website and landing-page forms. Adds the enquiry as a new lead, or, when the organization already has a lead with this phone number or email (matched ignoring case), fills in that lead instead: only fields it has empty are filled, and values that differ from what your team entered are kept and listed on the lead’s timeline. The owner, stage and pipeline of an existing lead are never changed, and an archived lead is not changed at all (the enquiry is noted on its timeline). A new lead starts in the starting stage and gets an owner from the organization’s assignment rules. Every capture is recorded on the lead’s timeline, naming the API key that sent it.\n\nServer to server only: call it from your website’s server (a route handler, a server action, PHP), never from browser JavaScript, where the key would be visible to anyone. There is no CORS support on purpose.\n\nRetries are safe: send an `Idempotency-Key` header (or an `externalId`), and a repeat within 24 hours returns the first result without capturing again.\n\nScope: `leads.capture` or `leads.write`.","security":[{"ApiKey":["leads.capture"]},{"ApiKey":["leads.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. A unique value per submission, such as a UUID; remembered per API key. A retry with the same key within 24 hours returns the first result, with `Idempotent-Replayed: true`; the same key with a different body is refused.","schema":{"type":"string","minLength":1,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The person’s name."},"phone":{"type":"string","minLength":6,"maxLength":32,"description":"With the country code, e.g. +919812345678. Numbers without one are read as Indian. Used, with email, to find a lead the organization already has."},"email":{"description":"Also used to find an existing lead when the phone number does not match one.","type":"string","maxLength":254},"source":{"description":"The source’s name, e.g. \"Website\" (the default) or \"Google Ads\". It must be one of the organization’s active sources (matched ignoring case; list them with GET /api/v1/lead-sources). Any other name is recorded as \"Website\", so a website key cannot fill the organization’s source list.","type":"string","minLength":1,"maxLength":60},"pipelineId":{"description":"For a new lead, the pipeline it starts in (IDs from GET /api/v1/pipelines); it lands in that pipeline’s starting stage. Defaults to the organization’s default pipeline. Never moves an existing lead.","type":"integer","exclusiveMinimum":0,"maximum":2147483647},"notes":{"description":"Free text shown on the lead’s timeline with this enquiry, such as the visitor’s message.","type":"string","maxLength":5000},"fields":{"description":"More details, by field key: the organization’s built-in and custom lead fields (GET /api/v1/lead-fields). Values are checked with the same rules as the CRM’s forms: dropdowns take one of their options (value or label), numbers, dates (YYYY-MM-DD) and yes/no fields are parsed. An unknown key is refused with the list of valid keys. On an existing lead only empty fields are filled.","type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}},{"type":"null"}]}},"attribution":{"description":"Where the enquiry came from. Shown on the lead’s timeline.","$ref":"#/components/schemas/CaptureAttribution"},"externalId":{"description":"Your own ID for this submission. A resend with the same externalId (per API key) within 24 hours returns the first result instead of capturing again.","type":"string","minLength":1,"maxLength":200}},"required":["name","phone"],"additionalProperties":false},"example":{"name":"Priya Sharma","phone":"+919812345678","email":"priya@example.com","source":"Website","notes":"Wants to know the fee structure for 2027.","fields":{"city":"Hyderabad","state":"Telangana","interest":"Premium plan","preferred_time":"evening"},"attribution":{"pageUrl":"https://www.example.com/pricing?utm_source=google&utm_medium=cpc","pageTitle":"Pricing","referrer":"https://www.google.com/","utmSource":"google","utmMedium":"cpc","utmCampaign":"mbbs-2027","formName":"Footer enquiry"},"externalId":"web-48213"}}}},"responses":{"200":{"description":"The organization already had this lead; it was found and any empty fields filled in. `Location` points at it.","content":{"application/json":{"schema":{"type":"object","properties":{"lead":{"type":"object","properties":{"id":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647,"description":"The lead’s ID, new or existing."},"created":{"type":"boolean","description":"True when a new lead was created."},"updated":{"type":"boolean","description":"True when an existing lead was found and at least one empty field on it was filled."},"archived":{"description":"Present when the person matched an archived lead: nothing on it was changed, the enquiry was noted on its timeline, and there is no `Location`.","type":"boolean","const":true}},"required":["id","created","updated"],"additionalProperties":false}},"required":["lead"],"additionalProperties":false}}}},"201":{"description":"A new lead was created. `Location` points at it.","content":{"application/json":{"schema":{"type":"object","properties":{"lead":{"type":"object","properties":{"id":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647,"description":"The lead’s ID, new or existing."},"created":{"type":"boolean","description":"True when a new lead was created."},"updated":{"type":"boolean","description":"True when an existing lead was found and at least one empty field on it was filled."},"archived":{"description":"Present when the person matched an archived lead: nothing on it was changed, the enquiry was noted on its timeline, and there is no `Location`.","type":"boolean","const":true}},"required":["id","created","updated"],"additionalProperties":false}},"required":["lead"],"additionalProperties":false},"example":{"lead":{"id":1842,"created":true,"updated":false}}}}},"400":{"description":"INVALID_IDEMPOTENCY_KEY: The Idempotency-Key header is longer than 200 characters or not plain ASCII.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"IDEMPOTENCY_IN_PROGRESS: A request with this Idempotency-Key is still being handled. Retry in a few seconds. Another request saved the same person at the same moment. Retry; it will find that lead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"VALIDATION_FAILED: A value is missing or invalid, a key in `fields` is not one of the organization’s active fields (`details.validKeys` lists them), or a field the organization requires for a new lead is missing. This Idempotency-Key was used within 24 hours for a different body. `pipelineId` is not one of the organization’s pipelines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"NOT_READY: The organization has no stage for new leads, or no active user to own them. Retry after it is set up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"typescript","label":"Next.js","source":"// app/api/enquiry/route.ts on your website. It runs on the server, so the key stays secret.\n// Your form posts to /api/enquiry; this sends the enquiry on to the CRM.\nexport async function POST(request: Request) {\n  const form = await request.json();\n  // One ID per submission. Send the same one again if you retry, and the CRM captures it once.\n  const submissionId = form.submissionId ?? crypto.randomUUID();\n\n  const response = await fetch(\"https://app.marro.si/api/v1/leads/capture\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Bearer ${process.env.CRM_API_KEY}`,\n      \"Content-Type\": \"application/json\",\n      \"Idempotency-Key\": submissionId,\n    },\n    body: JSON.stringify({\n      name: form.name,\n      phone: form.phone,\n      email: form.email,\n      source: \"Website\",\n      fields: {\n        \"city\": form.city,\n        \"interest\": form.interest,\n      },\n      attribution: {\n        pageUrl: form.pageUrl,\n        referrer: form.referrer,\n        formName: \"Contact form\",\n      },\n    }),\n    signal: AbortSignal.timeout(10_000),\n  });\n\n  if (!response.ok) {\n    // Keep the enquiry (a sheet, a database row, an email) and retry later with the same\n    // submissionId, so no lead is lost while the CRM cannot be reached.\n    console.error(\"CRM capture failed\", response.status, await response.text());\n  }\n  return Response.json({ ok: true });\n}"},{"lang":"php","label":"PHP / WordPress","source":"<?php\n// In wp-config.php (never in a theme file anyone can read, never in JavaScript):\n// define('CRM_API_KEY', 'mk_live_...');\n\n$response = wp_remote_post('https://app.marro.si/api/v1/leads/capture', [\n    'timeout' => 10,\n    'headers' => [\n        'Authorization'   => 'Bearer ' . CRM_API_KEY,\n        'Content-Type'    => 'application/json',\n        // One ID per submission; reuse it if you retry and the CRM captures it once.\n        'Idempotency-Key' => wp_generate_uuid4(),\n    ],\n    'body' => wp_json_encode([\n        'name'   => sanitize_text_field($_POST['name'] ?? ''),\n        'phone'  => sanitize_text_field($_POST['phone'] ?? ''),\n        'email'  => sanitize_email($_POST['email'] ?? ''),\n        'source' => 'Website',\n        'fields' => [\n            'city' => sanitize_text_field($_POST['city'] ?? ''),\n            'interest' => sanitize_text_field($_POST['interest'] ?? ''),\n        ],\n        'attribution' => [\n            'pageUrl'  => esc_url_raw(wp_get_referer() ?: ''),\n            'formName' => 'Contact form',\n        ],\n    ]),\n]);\n\nif (is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 300) {\n    // Keep the enquiry (for example, email it to yourself) so no lead is lost.\n    error_log('CRM capture failed: ' . (is_wp_error($response) ? $response->get_error_message() : wp_remote_retrieve_body($response)));\n}"},{"lang":"bash","label":"cURL","source":"curl -X POST \"https://app.marro.si/api/v1/leads/capture\" \\\n  -H \"Authorization: Bearer $CRM_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -d '{\n  \"name\": \"Priya Sharma\",\n  \"phone\": \"+919812345678\",\n  \"email\": \"priya@example.com\",\n  \"source\": \"Website\",\n  \"fields\": {\n    \"city\": \"Hyderabad\",\n    \"interest\": \"Premium plan\"\n  },\n  \"attribution\": {\n    \"pageUrl\": \"https://www.example.com/contact\",\n    \"formName\": \"Contact form\"\n  }\n}'"}]}},"/api/v1/leads/{leadId}":{"get":{"operationId":"get-lead","tags":["Leads"],"summary":"Get a lead","description":"One lead with every field.\n\nScope: `leads.read`.","security":[{"ApiKey":["leads.read"]}],"parameters":[{"name":"leadId","in":"path","required":true,"description":"The lead’s ID.","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"The lead.","content":{"application/json":{"schema":{"type":"object","properties":{"lead":{"$ref":"#/components/schemas/Lead"}},"required":["lead"],"additionalProperties":false},"example":{"lead":{"id":1842,"name":"Priya Sharma","phone":"+919812345678","email":"priya@example.com","leadType":"student","stageId":7,"pipelineId":2,"sourceId":3,"assignedTo":21,"stage":{"id":7,"name":"Follow Up","meaning":"open"},"pipeline":{"id":2,"name":"Main pipeline"},"source":{"id":3,"name":"Website"},"owner":{"id":21,"name":"Ravi Kumar"},"fields":{"city":"Hyderabad","interest":"Premium plan","preferred_time":"evening"},"createdAt":"2026-09-20T04:45:00Z","updatedAt":"2026-09-24T03:32:11Z"}}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND: No lead with this ID in the organization, or it is archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"update-lead","tags":["Leads"],"summary":"Update a lead","description":"Changes only the fields you send, checked with the organization’s field rules, and optionally the stage or the pipeline (`pipelineId`, with `stageId` for where in it). Every change is recorded on the lead’s timeline as made via the API.\n\nScope: `leads.write`.","security":[{"ApiKey":["leads.write"]}],"parameters":[{"name":"leadId","in":"path","required":true,"description":"The lead’s ID.","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"description":"Only the fields to change, by key. Send null to clear a field.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}},{"type":"null"}]}},"stageId":{"description":"A stage of another pipeline moves the lead into that pipeline.","type":"integer","exclusiveMinimum":0,"maximum":2147483647},"pipelineId":{"description":"Moves the lead to this pipeline, at `stageId` (one of its stages) or its starting stage.","type":"integer","exclusiveMinimum":0,"maximum":2147483647}},"additionalProperties":false},"example":{"fields":{"preferred_time":"morning","assigned_to":24},"stageId":9}}}},"responses":{"200":{"description":"The lead after the change.","content":{"application/json":{"schema":{"type":"object","properties":{"lead":{"$ref":"#/components/schemas/Lead"}},"required":["lead"],"additionalProperties":false},"example":{"lead":{"id":1842,"name":"Priya Sharma","phone":"+919812345678","email":"priya@example.com","leadType":"student","stageId":9,"pipelineId":2,"sourceId":3,"assignedTo":24,"stage":{"id":7,"name":"Follow Up","meaning":"open"},"pipeline":{"id":2,"name":"Main pipeline"},"source":{"id":3,"name":"Website"},"owner":{"id":21,"name":"Ravi Kumar"},"fields":{"city":"Hyderabad","interest":"Premium plan","preferred_time":"evening"},"createdAt":"2026-09-20T04:45:00Z","updatedAt":"2026-09-24T03:32:11Z"}}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND: No lead with this ID in the organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"DUPLICATE_LEAD: The new phone number or email belongs to another lead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"VALIDATION_FAILED: A field is invalid, or a required field would be emptied; `details.fields` says which. A refused stage, pipeline, owner or source gives `details.reason` (for example `STAGE_NOT_IN_PIPELINE`) and `details.field`. Nothing is changed when a request is refused this way.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/leads/{leadId}/notes":{"post":{"operationId":"add-note","tags":["Leads"],"summary":"Add a note","description":"Adds a note to the lead’s timeline, shown as added via the API.\n\nScope: `leads.write`.","security":[{"ApiKey":["leads.write"]}],"parameters":[{"name":"leadId","in":"path","required":true,"description":"The lead’s ID.","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":5000}},"required":["text"],"additionalProperties":false},"example":{"text":"Asked for the fee structure by email."}}}},"responses":{"201":{"description":"The note.","content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"object","properties":{"leadId":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},"text":{"type":"string"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["leadId","text","createdAt"],"additionalProperties":false}},"required":["note"],"additionalProperties":false},"example":{"note":{"leadId":1842,"text":"Asked for the fee structure by email.","createdAt":"2026-09-24T03:35:00Z"}}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"NOT_FOUND: No lead with this ID in the organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/lead-fields":{"get":{"operationId":"list-fields","tags":["Reference"],"summary":"List lead fields","description":"The organization’s active lead fields, its own custom fields included: their keys, types, options and whether each is required. Use the keys in `fields` when reading, writing and capturing leads. A `leads.capture` key may read this list too, to build a form. Fields an organization adds later appear here at once and are accepted straight away.\n\nScope: `leads.read` or `leads.capture`.","security":[{"ApiKey":["leads.read"]},{"ApiKey":["leads.capture"]}],"responses":{"200":{"description":"Fields in form order.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/LeadField"}}},"required":["items"],"additionalProperties":false},"example":{"items":[{"key":"preferred_time","label":"Preferred time to call","type":"select","section":"Preferences","storage":"custom","options":[{"value":"morning","label":"Morning"},{"value":"evening","label":"Evening"}],"requiredOnCreate":false,"requiredOnEdit":false,"showWhen":null}]}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/lead-stages":{"get":{"operationId":"list-stages","tags":["Reference"],"summary":"List stages","description":"Active stages of every pipeline, each pipeline’s in order, with what each means (open, won or lost), which one its new leads start in, and the pipeline it belongs to.\n\nScope: `leads.read`.","security":[{"ApiKey":["leads.read"]}],"responses":{"200":{"description":"Stages in pipeline order.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}}},"required":["items"],"additionalProperties":false},"example":{"items":[{"id":5,"name":"New Lead","slug":"new_lead","order":1,"meaning":"open","isDefault":true,"pipelineId":2},{"id":11,"name":"Admission Done","slug":"admission_done","order":5,"meaning":"won","isDefault":false,"pipelineId":2}]}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pipelines":{"get":{"operationId":"list-pipelines","tags":["Reference"],"summary":"List pipelines","description":"The organization’s pipelines in order: separate tracks for leads, each with its own stages. New leads go into the default one unless a `pipelineId` or a stage of another pipeline is given.\n\nScope: `leads.read`.","security":[{"ApiKey":["leads.read"]}],"responses":{"200":{"description":"Pipelines in order.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Pipeline"}}},"required":["items"],"additionalProperties":false},"example":{"items":[{"id":2,"name":"Main pipeline","order":1,"isDefault":true},{"id":9,"name":"Visa services","order":2,"isDefault":false}]}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/lead-sources":{"get":{"operationId":"list-sources","tags":["Reference"],"summary":"List sources","description":"Active lead sources. Send an ID as the `source` field when creating or updating a lead.\n\nScope: `leads.read`.","security":[{"ApiKey":["leads.read"]}],"responses":{"200":{"description":"Sources by name.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/NamedRef"}}},"required":["items"],"additionalProperties":false},"example":{"items":[{"id":3,"name":"Website"},{"id":4,"name":"Meta ads"}]}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/users":{"get":{"operationId":"list-users","tags":["Reference"],"summary":"List people","description":"Active people in the organization, to read lead owners and to send `assigned_to`. Names only; no contact details.\n\nScope: `leads.read`.","security":[{"ApiKey":["leads.read"]}],"responses":{"200":{"description":"People by name.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/NamedRef"}}},"required":["items"],"additionalProperties":false},"example":{"items":[{"id":21,"name":"Ravi Kumar"},{"id":24,"name":"Asha Rao"}]}}}},"401":{"description":"UNAUTHORIZED: The key is missing, wrong, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"FORBIDDEN: The key lacks the scope, or the organization’s plan does not include it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"RATE_LIMITED: More than 600 requests in an hour with this key, or 3000 with all of the organization’s keys together. `Retry-After` says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"INTERNAL: Something failed on our side. Retry; if it persists, contact support quoting the `X-Request-Id` response header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","description":"An organization API key, created in Settings → Connections → API keys. Keys start with `mk_live_`. Marro stores only a hash of a key, so it is shown once, when it is created: copy it then. Revoking a key ends its access at once. To replace a key without downtime, create the new one, switch your server to it, then revoke the old one. Changes made with a key appear on the lead’s timeline as made by the person who created the key, via the API. Scopes: `leads.read` (Read leads), `leads.write` (Create and update leads), `leads.capture` (Capture leads).","x-scopes":{"leads.read":{"label":"Read leads","description":"Leads with every field, and the lists that describe them: fields, stages, sources and people.","module":"crm"},"leads.write":{"label":"Create and update leads","description":"Create leads, change their fields, stage and owner, and add notes. Needs leads.read.","module":"crm"},"leads.capture":{"label":"Capture leads","description":"Capture leads from a website or form: add a lead or update the existing one with the same phone or email. Cannot read, list or change anything else.","module":"crm"}},"x-key-types":[{"name":"Website form","scopes":["leads.capture"],"lifetimeDays":730,"description":"A website or landing-page form that sends enquiries in as leads. Can do nothing else."},{"name":"Another application","lifetimeDays":90,"description":"Any other integration. Choose the scopes it needs when you create it."}]}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_FAILED, DUPLICATE_LEAD, RATE_LIMITED…"},"message":{"type":"string","description":"A sentence for people."},"details":{"description":"For VALIDATION_FAILED, `fields` maps each field key to its problem.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false,"id":"Error"},"Lead":{"type":"object","properties":{"id":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},"name":{"type":"string"},"phone":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"leadType":{"type":"string","enum":["student","consultant"]},"stageId":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},{"type":"null"}]},"pipelineId":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647,"description":"The pipeline the lead is in (see GET /api/v1/pipelines). Its stage is always one of this pipeline’s stages."},"sourceId":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},{"type":"null"}]},"assignedTo":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},{"type":"null"}],"description":"The owner’s user ID."},"stage":{"anyOf":[{"$ref":"#/components/schemas/StageRef"},{"type":"null"}]},"pipeline":{"anyOf":[{"$ref":"#/components/schemas/NamedRef"},{"type":"null"}]},"source":{"anyOf":[{"$ref":"#/components/schemas/NamedRef"},{"type":"null"}]},"owner":{"anyOf":[{"$ref":"#/components/schemas/NamedRef"},{"type":"null"}]},"fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}},{"type":"null"}]},"description":"Every other active field, by field key (see GET /api/v1/lead-fields), including the organization’s own custom fields. Empty fields are omitted."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["id","name","phone","email","leadType","stageId","pipelineId","sourceId","assignedTo","stage","pipeline","source","owner","fields","createdAt","updatedAt"],"additionalProperties":false,"id":"Lead"},"StageRef":{"type":"object","properties":{"id":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},"name":{"type":"string"},"meaning":{"type":"string","enum":["open","won","lost"],"description":"What reaching the stage means in this organization."}},"required":["id","name","meaning"],"additionalProperties":false,"id":"StageRef"},"NamedRef":{"type":"object","properties":{"id":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false,"id":"NamedRef"},"CaptureAttribution":{"type":"object","properties":{"pageUrl":{"description":"The full address of the page the form was on, with its query string.","type":"string","maxLength":2000},"pageTitle":{"type":"string","maxLength":300},"referrer":{"description":"The page the visitor came from (document.referrer).","type":"string","maxLength":2000},"utmSource":{"type":"string","maxLength":200},"utmMedium":{"type":"string","maxLength":200},"utmCampaign":{"type":"string","maxLength":200},"utmTerm":{"type":"string","maxLength":200},"utmContent":{"type":"string","maxLength":200},"formName":{"description":"Which form or button on the site, e.g. \"Footer enquiry\".","type":"string","maxLength":200}},"additionalProperties":false,"id":"CaptureAttribution"},"LeadField":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string","enum":["text","textarea","number","date","phone","email","url","select","multiselect","checkbox","lead_source","user"]},"section":{"type":"string"},"storage":{"type":"string","enum":["column","custom"],"description":"`custom` fields were created by the organization."},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false},"description":"For select and multiselect: send the `value`."},"requiredOnCreate":{"type":"boolean"},"requiredOnEdit":{"type":"boolean"},"showWhen":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"equals":{"type":"array","items":{"type":"string"}}},"required":["field","equals"],"additionalProperties":false},{"type":"null"}],"description":"Asked for only when another field has one of these values."}},"required":["key","label","type","section","storage","options","requiredOnCreate","requiredOnEdit","showWhen"],"additionalProperties":false,"id":"LeadField"},"Stage":{"type":"object","properties":{"id":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},"name":{"type":"string"},"slug":{"type":"string"},"order":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"meaning":{"type":"string","enum":["open","won","lost"]},"isDefault":{"type":"boolean","description":"New leads in its pipeline start here."},"pipelineId":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647,"description":"The pipeline the stage belongs to."}},"required":["id","name","slug","order","meaning","isDefault","pipelineId"],"additionalProperties":false,"id":"Stage"},"Pipeline":{"type":"object","properties":{"id":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647},"name":{"type":"string"},"order":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isDefault":{"type":"boolean","description":"New leads go into this pipeline unless told otherwise."}},"required":["id","name","order","isDefault"],"additionalProperties":false,"id":"Pipeline"}}},"x-rate-limits":[{"label":"Per key","limit":600,"window":"hour"},{"label":"Per organization, all keys together","limit":3000,"window":"hour"}],"x-max-body-bytes":100000,"x-transport-errors":[{"status":400,"code":"KEY_IN_URL","when":"A key, or a parameter such as `api_key` or `token`, is in the URL. Keys go only in the Authorization header; replace a key that has been in a URL."},{"status":403,"code":"HTTPS_REQUIRED","when":"The request was made over plain HTTP."},{"status":413,"code":"PAYLOAD_TOO_LARGE","when":"The body is over 100 KB."},{"status":415,"code":"UNSUPPORTED_MEDIA_TYPE","when":"A body was sent without `Content-Type: application/json`."},{"status":400,"code":"INVALID_JSON","when":"The body is not valid JSON."}]}