VitrinaAPI
Webhooks

Event catalogue

Every event this API delivers, with its payload and a sample — generated from the same catalogue GET /webhooks/events answers with.

This page is generated from the catalogue GET /api/v1/webhooks/events serves. It cannot describe an event that does not exist, and it goes stale only if the catalogue does.

The chapter that explains how to subscribe, verify the signature and read the delivery log is here.

The envelope

Every event arrives with the same body. The fields below are the envelope; data is what changes per event, and it arrives only when the subscription asked for it and its owner may read it.

FieldWhat it carries
idstring (uuid) — unique event id; use for idempotency
typestring — one of the events above
versioninteger — the schema version of this event type; bumped only on a breaking change to data
livemodeboolean — false when this event came from a sandbox (test-mode) workspace, true otherwise. Mirrored on the Vitrina-Livemode header; since livemode is in the signed body, verifying the signature verifies this too.
created_atISO 8601 timestamp
tenant_idstring (uuid) — your workspace id
resourceobject { type: string, id: string | null, url: string | null } — what the event is about; GET url with your credential to read it
changesobject | absent — field → { from, to } for the identifiers and states that changed
authorobject { kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact', id: string | null, name: string | null, via?: { kind: 'connected_app' | 'personal_token', name: string } } — who did it
dataobject | absent — the resource payload (see catalog); present only when the subscription includes data and its owner may read it
data_omittedstring | absent — why data is not in this delivery; exactly one of data / data_omitted is present

Notice or data

Every delivery carries exactly one of data or data_omitted. These are the two modes and every reason data can be missing:

Mode or reasonWhat it carries
noticeThe default. Every delivery carries the notice — resource, changes, author, time, url — and data_omitted: "not_requested". Read the resource with your own credential, which applies scopes, visibility and the access log.
data«Incluir datos del recurso» (include_data: true). data is added when, at dispatch time, the subscription owner holds the resource read scope and sees every record; otherwise the delivery is a notice with the reason in data_omitted.
not_requestedThe subscription did not ask for data.
sensitiveThe event is about a dato sensible (health data). It is always a notice, whatever the subscription asked for.
missing_scope:<scope>The owner does not hold that read scope (e.g. missing_scope:contacts:read).
restricted_visibilityThe owner sees only some records (assigned-only, or some sucursales).
owner_unavailableThe owner is no longer an active member, or its API key was revoked or expired. Recreate the subscription, or edit its URL or events, to make yourself its owner.

The headers

HeaderWhat it carries
X-Webhook-EventThe event name (e.g. ticket.opened).
X-Webhook-Event-IdUnique event id. Retries reuse this id so you can dedupe.
X-Webhook-TimestampUnix seconds when the signature was generated.
X-Webhook-Signaturet=<unix>,v1=<hex> — HMAC-SHA256 of ${timestamp}.${rawBody} with your subscription secret.
Vitrina-Livemodetrue or false, mirroring the body's livemode. Signed as part of the body — verifying the signature verifies this too.

The signature

hmac-sha256, t=<unix>,v1=<hex>.

Replay window: 300 seconds. A signature older than that is rejected.

Verifying, in pseudocode

const [ts, sig] = parseHeader('X-Webhook-Signature');  // 't=…,v1=…'
const expected = hmac_sha256(secret, ts + '.' + rawBody);  // hex
if (!timing_safe_equal(sig.v1, expected)) reject(401);
if (Math.abs(now_unix() - ts) > 300) reject(401);  // 5-min replay window

Retries

Up to 5 attempts with exponential backoff: 5, 10, 20, 40, 80 seconds apart.

All retries reuse the same X-Webhook-Event-Id so your endpoint can be idempotent.

Auto-pause

A subscription pauses itself when 20 deliveries in a row exhaust their retries, or when it has been failing for 24 hours without a single successful response.

A subscription is paused when 20 events in a row exhaust their retries, or when it has failed for 24 hours without a single success. The workspace admins are notified once. A paused subscription receives nothing until POST /webhooks/{id}/resume; events in between are not queued — read the resources to catch up, or redeliver from the delivery log.

The events

156 events. The name is what goes in events when you create the subscription, and what arrives in X-Webhook-Event.

campaign.sent

A campaign finished sending — every recipient reached a terminal pre-delivery state (sent/failed/suppressed).

Version 1 · Resource campaign · data requires campaigns:read

Fires when. The campaign worker drains the last pending recipient and flips the campaign to status=sent.

FieldType
campaign_idstring (uuid)
namestring
channelenum: whatsapp | email
total_recipientsinteger
sentinteger (includes delivered/read)
failedinteger
suppressedinteger
finished_atISO 8601 timestamp | null
{
  "campaign_id": "0d9b2a4e-1f6c-4e7a-9b3d-5c8e2f1a6b4d",
  "name": "Reactivación de clientes abril",
  "channel": "whatsapp",
  "total_recipients": 1240,
  "sent": 1198,
  "failed": 17,
  "suppressed": 25,
  "finished_at": "2026-06-11T15:04:05Z"
}

campaign.paused

A sending campaign was paused automatically — the WhatsApp number hit its Meta messaging limit, or the email reputation guard tripped (ADR 0032: >8% hard bounces or >0.3% complaints after 200 sends; also blocks the tenant email channel).

Version 1 · Resource campaign · data requires campaigns:read

Fires when. A campaign send gets a rate/messaging-limit error from Atribu/Meta (auto-probes hourly), or the SES event ingest crosses a reputation threshold.

FieldType
campaign_idstring (uuid)
namestring
reasonenum: messaging_limit | reputation_guard
detailstring | undefined (which threshold, at what rate)
{
  "campaign_id": "0d9b2a4e-1f6c-4e7a-9b3d-5c8e2f1a6b4d",
  "name": "Reactivación de clientes abril",
  "reason": "messaging_limit"
}

sending_domain.verified

A tenant email sending domain finished DKIM verification and can now send email campaigns (ADR 0032).

Version 1 · Resource sending_domain · data requires messaging_accounts:read

Fires when. A sending-domain refresh observes SES flip VerifiedForSendingStatus to true for the first time.

FieldType
idstring (uuid)
domainstring
{
  "id": "7f2c1b9e-3a5d-4c8e-9f1a-2b6d4e8c0a3f",
  "domain": "mail.cliente.cl"
}

coach.proposals_ready

Conversation Coach generated new improvement proposals for an agent — route to Slack/email/etc. to alert the agent owner.

Version 1 · Resource ai_agent · data requires ai_agents:read

Fires when. A finding investigation or a correction synthesis persisted at least one improvement_proposal.

FieldType
ai_agent_idstring (uuid)
finding_idstring (uuid) | undefined (present when source=investigation)
correction_idstring (uuid) | undefined (present when source=correction)
sourceenum: review | investigation | correction
proposalsnumber (count of new proposals)
{
  "ai_agent_id": "4ce54eaf-…",
  "finding_id": "00000000-…-0000000f1nd1",
  "source": "investigation",
  "proposals": 3
}

coach.finding_ready

The conversation reviewer detected a RECURRING issue with an agent (Mejoras, ADR 0095) — «promete descuentos que no existen», with an occurrence count — one step before anybody proposes a fix for it.

Version 1 · Resource ai_agent · data requires ai_agents:read

Fires when. A conversation review created or bumped a finding.

FieldType
ai_agent_idstring (uuid)
findingsarray of { id: string (uuid), kind: string, topic_key: string, title: string, occurrence_count: number, status: string }
sourceenum: review (the only producer today)
{
  "ai_agent_id": "4ce54eaf-…",
  "findings": [
    {
      "id": "00000000-…-0000000f1nd1",
      "kind": "missing_knowledge",
      "topic_key": "financiamiento.pie_minimo",
      "title": "No sabe responder por el pie mínimo",
      "occurrence_count": 7,
      "status": "open"
    }
  ],
  "source": "review"
}

voice.autonomous_call

A proactive/autonomous voice agent initiated an outbound speed-to-lead call on its own (ADR-0016) — route to Slack/email so a human is aware ("warn and be careful").

Version 1 · Resource lead · data requires leads:read

Fires when. A new lead arrives and the tenant's voice agent autonomy_level is proactive or autonomous and all guardrails pass.

FieldType
lead_idstring (uuid)
contact_idstring (uuid) | null
agent_idstring (uuid)
autonomy_levelenum: proactive | autonomous
{
  "lead_id": "4ce54eaf-…",
  "contact_id": "00000000-…",
  "agent_id": "a1b2…",
  "autonomy_level": "proactive"
}

safety_hold.placed

A clinical SAFETY HOLD was placed on a contact: proactive messaging to that person is blocked until a named human acknowledges and resolves it. Opt-in per tenant (clinical_safety.operational_alert_webhook).

Version 1 · Resource outbound_hold · data requires outbound_holds:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. The service-lifecycle evaluator decides a patient reply requires professional attention, or an operator places a hold by hand. Fires once per hold — a re-raised concern deduplicates onto the live hold.

FieldType
hold_idstring (uuid)
contact_idstring (uuid)
scopeenum: run | contact | subject
kindenum: safety | identity | manual | policy
reasonstring
placed_atISO 8601 timestamp
stepKeystring | null
ticketIdstring | null
ackMinutesinteger (the acknowledgement target that now applies)
{
  "hold_id": "018f…",
  "contact_id": "9a1c…",
  "scope": "contact",
  "kind": "safety",
  "reason": "the patient reports worsening swelling",
  "placed_at": "2027-02-09T14:03:11.000Z",
  "stepKey": "check_in_24h",
  "ticketId": "T-1247",
  "ackMinutes": 15
}

safety_hold.ack_overdue

Nobody acknowledged a safety hold within the tenant's acknowledgement target (D6 default: 15 minutes of declared coverage). The backup has been paged.

Version 1 · Resource outbound_hold · data requires outbound_holds:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. The safety-hold SLA sweep (5-min tick) finds a hold still in active past its ack target. Fires once per hold.

FieldType
hold_idstring (uuid)
contact_idstring (uuid)
eventTypeliteral: ack_overdue
targetMinutesinteger
elapsedMinutesinteger (coverage minutes, not wall-clock)
withinCoverageboolean — false means the tenant has declared no coverage schedule, which is a configuration problem rather than a missed target
{
  "hold_id": "018f…",
  "contact_id": "9a1c…",
  "eventType": "ack_overdue",
  "targetMinutes": 15,
  "elapsedMinutes": 22,
  "withinCoverage": true
}

safety_hold.first_response_overdue

No human wrote to the patient within the first-response target (D6 default: 60 minutes of declared coverage) after a safety hold was placed. A durable breach row was recorded.

Version 1 · Resource outbound_hold · data requires outbound_holds:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. The safety-hold SLA sweep finds no human outbound message to the contact dated after the hold was placed, past the first-response target. Fires once per hold.

FieldType
hold_idstring (uuid)
contact_idstring (uuid)
eventTypeliteral: first_response_overdue
targetMinutesinteger
elapsedMinutesinteger (coverage minutes, not wall-clock)
withinCoverageboolean
{
  "hold_id": "018f…",
  "contact_id": "9a1c…",
  "eventType": "first_response_overdue",
  "targetMinutes": 60,
  "elapsedMinutes": 74,
  "withinCoverage": true
}

chileautos.lead

A buyer lead arrived from Chileautos ("envío de leads") and was ingested as a Vitrina lead.

Version 1 · Resource lead · data requires leads:read

Fires when. The Chileautos leads webhook (POST /integrations/chileautos/leads) is processed by the worker.

FieldType
lead_idstring (uuid) | null
contact_idstring
vehicle_idstring (uuid) | null
sourceenum: chileautos | whatsapp | ChileautosLeadPremium | null
chileautos_lead_identifierstring
chileautos_statusstring | null
{
  "lead_id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "contact_id": "4821",
  "vehicle_id": "a35d4ff0-8612-4ff0-8612-a8ee57f4f095",
  "source": "chileautos",
  "chileautos_lead_identifier": "88d404ca-a35d-4ff0-8612-a8ee57f4f095",
  "chileautos_status": "New"
}

yapo.lead

A buyer lead arrived from Yapo.cl and was ingested as a Vitrina lead.

Version 1 · Resource lead · data requires leads:read

Fires when. The per-account yapo leads webhook (POST /integrations/yapo/leads/:accountId/:token) is processed by the worker.

FieldType
lead_idstring (uuid) | null
contact_idstring
vehicle_idstring (uuid) | null
yapo_lead_idstring
yapo_ad_idstring | null
{
  "lead_id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "contact_id": "4821",
  "vehicle_id": "a35d4ff0-8612-4ff0-8612-a8ee57f4f095",
  "yapo_lead_id": "90815723",
  "yapo_ad_id": "75481920"
}

website.lead

A buyer inquiry arrived through a dealer website's "Consulta del sitio" form and was ingested as a Vitrina lead.

Version 1 · Resource lead · data requires leads:read

Fires when. POST /api/v1/website-leads (Bearer api_key, scope leads:intake) is processed.

FieldType
lead_idstring (uuid) | null
contact_idstring
conversation_idstring | null
vehicle_idstring (uuid) | null
submission_idstring
intentenum: buy | sell | financing | trade_in (defaults to buy)
{
  "lead_id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "contact_id": "4821",
  "conversation_id": "9b1e2a4e-1f6c-4e7a-9b3d-5c8e2f1a6b4d",
  "vehicle_id": "a35d4ff0-8612-4ff0-8612-a8ee57f4f095",
  "submission_id": "3f7a1c9e2b4d5f6a7b8c9d0e1f2a3b4c",
  "intent": "sell"
}

consignment.candidate.created

A private owner offered their car through a dealer website's "vende tu auto" form and the submission landed as a consignment candidate.

Version 1 · Resource consignment_candidate · data requires consignments:read

Fires when. POST /api/v1/widget/consignments (publishable pk_ key, origin-locked, scope leads:intake) is processed. A honeypot-tripped or duplicate submission fires nothing.

FieldType
candidate_idstring (uuid)
patentestring (as typed by the owner)
makestring
modelstring
yearinteger
kilometersinteger
expected_price_clpinteger (whole CLP) | null
sale_horizonenum: 7d | 15d | 30d | cotizando
region_codestring (2-digit CUT)
photo_countinteger
submission_idstring
{
  "candidate_id": "d41f2b8e-5a17-4c93-8b0e-6f1a2c3d4e5f",
  "patente": "KXTR52",
  "make": "Toyota",
  "model": "Yaris",
  "year": 2019,
  "kilometers": 68000,
  "expected_price_clp": 9500000,
  "sale_horizon": "15d",
  "region_code": "13",
  "photo_count": 3,
  "submission_id": "8b1c0a5d7e2f4a6b9c3d0e1f2a3b4c5d"
}

quote.issued

A cotización was issued — a price offer on a unit, frozen at issuance and holding no reservation on the car.

Version 1 · Resource quote · data requires quotes:read

Fires when. POST /api/v1/quotes.

FieldType
idstring (uuid)
display_idstring (Q-…)
vehicle_idstring (uuid)
buyer_contact_idstring (uuid)
salesperson_idstring (uuid) | null
offered_price_clpinteger (whole CLP)
tax_treatmentenum: afecto | exento | pass_through
expires_onstring (YYYY-MM-DD)
stateenum: vigente | expirada | nula
{
  "id": "d2d2d2d2-0000-4000-8000-000000000001",
  "display_id": "Q-47",
  "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
  "buyer_contact_id": "22222222-0000-4000-8000-000000000002",
  "salesperson_id": "11111111-0000-4000-8000-000000000001",
  "offered_price_clp": 11900000,
  "tax_treatment": "afecto",
  "expires_on": "2026-09-23",
  "state": "vigente"
}

quote.voided

A cotización was voided. Terminal — a voided quote is never restored.

Version 1 · Resource quote · data requires quotes:read

Fires when. POST /api/v1/quotes/{id}/void.

FieldType
idstring (uuid)
display_idstring (Q-…)
void_reasonstring
voided_bystring (uuid)
voided_atstring (ISO 8601)
{
  "id": "d2d2d2d2-0000-4000-8000-000000000002",
  "display_id": "Q-52",
  "void_reason": "el cliente desistió de la compra",
  "voided_by": "11111111-0000-4000-8000-000000000001",
  "voided_at": "2026-09-15T11:20:03.000Z"
}

quote.extended

A cotización’s validity window was moved to a new date, including bringing an expired one back to vigente.

Version 1 · Resource quote · data requires quotes:read

Fires when. POST /api/v1/quotes/{id}/extend.

FieldType
idstring (uuid)
display_idstring (Q-…)
expires_onstring (YYYY-MM-DD)
extend_reasonstring
extended_bystring (uuid)
extended_atstring (ISO 8601)
{
  "id": "d2d2d2d2-0000-4000-8000-000000000001",
  "display_id": "Q-47",
  "expires_on": "2026-10-20",
  "extend_reason": "el cliente sigue esperando la aprobación del crédito",
  "extended_by": "11111111-0000-4000-8000-000000000001",
  "extended_at": "2026-09-20T09:11:44.000Z"
}

quote.archived

A cotización was filed away from the working register. Not a delete and not a state — its folio and state are unchanged.

Version 1 · Resource quote · data requires quotes:read

Fires when. POST /api/v1/quotes/{id}/archive.

FieldType
idstring (uuid)
display_idstring (Q-…)
archive_reasonstring
archived_bystring (uuid)
archived_atstring (ISO 8601)
{
  "id": "d2d2d2d2-0000-4000-8000-000000000003",
  "display_id": "Q-31",
  "archive_reason": "cotización antigua, ya no vigente en el seguimiento",
  "archived_by": "11111111-0000-4000-8000-000000000001",
  "archived_at": "2026-09-10T08:00:00.000Z"
}

reservation.created

A reserva took a hold on a unit — the only Wave 2a document that locks inventory.

Version 1 · Resource reservation · data requires reservations:read

Fires when. POST /api/v1/reservations.

FieldType
idstring (uuid)
display_idstring (R-…)
vehicle_idstring (uuid)
holder_contact_idstring (uuid)
salesperson_idstring (uuid) | null
agreed_price_clpinteger (whole CLP) | null
hold_expires_onstring (YYYY-MM-DD)
statusenum: activa | completada | anulada
{
  "id": "d3d3d3d3-0000-4000-8000-000000000001",
  "display_id": "R-18",
  "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
  "holder_contact_id": "22222222-0000-4000-8000-000000000002",
  "salesperson_id": "11111111-0000-4000-8000-000000000001",
  "agreed_price_clp": 11900000,
  "hold_expires_on": "2026-09-25",
  "status": "activa"
}

reservation.voided

A reserva was voided, releasing the hold on the unit for good.

Version 1 · Resource reservation · data requires reservations:read

Fires when. POST /api/v1/reservations/{id}/void.

FieldType
idstring (uuid)
display_idstring (R-…)
void_reasonstring
voided_bystring (uuid)
voided_atstring (ISO 8601)
{
  "id": "d3d3d3d3-0000-4000-8000-000000000002",
  "display_id": "R-22",
  "void_reason": "el cliente no completó el pago del abono a tiempo",
  "voided_by": "11111111-0000-4000-8000-000000000001",
  "voided_at": "2026-09-20T10:05:00.000Z"
}

reservation.abono_disposed

The deposit on a closed reserva was disposed of — kept, refunded or applied.

Version 1 · Resource reservation · data requires reservations:read

Fires when. POST /api/v1/reservations/{id}/abono-disposition.

FieldType
idstring (uuid)
display_idstring (R-…)
abono_dispositionenum: retenido | devuelto | aplicado
abono_disposition_reasonstring | null
abono_disposition_bystring (uuid)
abono_disposition_atstring (ISO 8601)
{
  "id": "d3d3d3d3-0000-4000-8000-000000000001",
  "display_id": "R-18",
  "abono_disposition": "devuelto",
  "abono_disposition_reason": "el cliente desistió; se devolvió el abono",
  "abono_disposition_by": "11111111-0000-4000-8000-000000000001",
  "abono_disposition_at": "2026-09-21T09:00:00.000Z"
}

reservation.converted

A reserva was converted into a nota de venta, closing the hold and carrying its payments onto the new document.

Version 1 · Resource reservation · data requires reservations:read

Fires when. POST /api/v1/reservations/{id}/convert.

FieldType
idstring (uuid)
display_idstring (R-…)
sale_note_idstring (uuid)
sale_note_display_idstring (V-…)
{
  "id": "d3d3d3d3-0000-4000-8000-000000000001",
  "display_id": "R-18",
  "sale_note_id": "d4d4d4d4-0000-4000-8000-000000000001",
  "sale_note_display_id": "V-118"
}

sale_note.issued

A nota de venta was issued, allocating its folio.

Version 1 · Resource sale_note · data requires sale_notes:read

Fires when. POST /api/v1/sale-notes.

FieldType
idstring (uuid)
display_idstring (V-…)
vehicle_idstring (uuid)
buyer_contact_idstring (uuid)
net_clpinteger (whole CLP)
tax_clpinteger (whole CLP) | null
tax_treatmentenum: afecto | exento | pass_through
statusenum: issued | approved | voided
{
  "id": "d4d4d4d4-0000-4000-8000-000000000001",
  "display_id": "V-118",
  "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000002",
  "buyer_contact_id": "22222222-0000-4000-8000-000000000002",
  "net_clp": 11900000,
  "tax_clp": 1900000,
  "tax_treatment": "afecto",
  "status": "issued"
}

sale_note.approved

A nota de venta was approved — the instant it becomes legally valid, the car is marked sold and its deal closes won.

Version 1 · Resource sale_note · data requires sale_notes:read

Fires when. POST /api/v1/sale-notes/{id}/approve.

FieldType
idstring (uuid)
display_idstring (V-…)
approved_bystring (uuid)
approved_atstring (ISO 8601)
{
  "id": "d4d4d4d4-0000-4000-8000-000000000001",
  "display_id": "V-118",
  "approved_by": "11111111-0000-4000-8000-000000000001",
  "approved_at": "2026-09-19T14:10:00.000Z"
}

sale_note.voided

A nota de venta was voided.

Version 1 · Resource sale_note · data requires sale_notes:read

Fires when. POST /api/v1/sale-notes/{id}/void.

FieldType
idstring (uuid)
display_idstring (V-…)
void_reasonstring
voided_bystring (uuid)
voided_atstring (ISO 8601)
{
  "id": "d4d4d4d4-0000-4000-8000-000000000001",
  "display_id": "V-118",
  "void_reason": "el banco rechazó el financiamiento del comprador",
  "voided_by": "11111111-0000-4000-8000-000000000001",
  "voided_at": "2026-09-20T09:00:00.000Z"
}

purchase_note.issued

A nota de compra was issued — the instrument that creates a vehicle’s cost basis.

Version 1 · Resource purchase_note · data requires purchase_notes:read

Fires when. POST /api/v1/purchase-notes.

FieldType
idstring (uuid)
display_idstring (P-…)
seller_contact_idstring (uuid)
net_clpinteger (whole CLP)
tax_treatmentenum: afecto | exento | no_gravado
statusenum: issued | voided
{
  "id": "d5d5d5d5-0000-4000-8000-000000000001",
  "display_id": "P-9",
  "seller_contact_id": "22222222-0000-4000-8000-000000000003",
  "net_clp": 6200000,
  "tax_treatment": "no_gravado",
  "status": "issued"
}

purchase_note.corrected

A live nota de compra was corrected, stamped with who changed it.

Version 1 · Resource purchase_note · data requires purchase_notes:read

Fires when. PATCH /api/v1/purchase-notes/{id}.

FieldType
idstring (uuid)
display_idstring (P-…)
corrected_bystring (uuid)
corrected_atstring (ISO 8601)
{
  "id": "d5d5d5d5-0000-4000-8000-000000000001",
  "display_id": "P-9",
  "corrected_by": "11111111-0000-4000-8000-000000000001",
  "corrected_at": "2026-09-13T09:00:00.000Z"
}

purchase_note.voided

A nota de compra was voided.

Version 1 · Resource purchase_note · data requires purchase_notes:read

Fires when. POST /api/v1/purchase-notes/{id}/void.

FieldType
idstring (uuid)
display_idstring (P-…)
void_reasonstring
voided_bystring (uuid)
voided_atstring (ISO 8601)
{
  "id": "d5d5d5d5-0000-4000-8000-000000000001",
  "display_id": "P-9",
  "void_reason": "se anuló la compra: el vendedor desistió antes de firmar",
  "voided_by": "11111111-0000-4000-8000-000000000001",
  "voided_at": "2026-09-14T11:30:00.000Z"
}

payment.recorded

A payment was recorded against a reservation or a nota de venta.

Version 1 · Resource document_payment · data requires document_payments:read

Fires when. POST /api/v1/document-payments (deprecated door) or POST /api/v1/payments.

FieldType
idstring (uuid)
reservation_idstring (uuid) | null
sale_note_idstring (uuid) | null
instrumentenum: efectivo | transferencia | tarjeta_debito | tarjeta_credito | cheque
amount_clpinteger (whole CLP)
paid_onstring (YYYY-MM-DD)
{
  "id": "d6d6d6d6-0000-4000-8000-000000000001",
  "reservation_id": "d3d3d3d3-0000-4000-8000-000000000001",
  "sale_note_id": null,
  "instrument": "transferencia",
  "amount_clp": 300000,
  "paid_on": "2026-09-18"
}

consignment.created

A consignment contract was created — the vehicle’s tenencia becomes consignacion.

Version 1 · Resource consignment_contract · data requires consignments:read

Fires when. POST /api/v1/consignments or POST /api/v1/consignments/bulk.

FieldType
idstring (uuid)
vehicle_idstring (uuid)
dueno_contact_idstring (uuid) | null
modalidadenum: en_local | virtual
comision_typeenum: percentage | fixed | null
comision_valuenumber | null
minimo_clpinteger (whole CLP) | null
estadoenum: activo | devuelto | vendido
{
  "id": "d7d7d7d7-0000-4000-8000-000000000001",
  "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
  "dueno_contact_id": "22222222-0000-4000-8000-000000000004",
  "modalidad": "en_local",
  "comision_type": "percentage",
  "comision_value": 10,
  "minimo_clp": 8000000,
  "estado": "activo"
}

consignment.returned

A consignment contract was returned — the car goes back to its owner and its tenencia becomes propio.

Version 1 · Resource consignment_contract · data requires consignments:read

Fires when. POST /api/v1/consignments/{id}/return or PATCH .../{id} with estado: "devuelto".

FieldType
idstring (uuid)
vehicle_idstring (uuid)
estadoenum: devuelto
{
  "id": "d7d7d7d7-0000-4000-8000-000000000001",
  "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
  "estado": "devuelto"
}

consignment.sold

A consigned car was sold and settled with its owner — the contract moves to vendido and produces a Liquidación.

Version 1 · Resource consignment_contract · data requires consignments:read

Fires when. POST /api/v1/consignments/{id}/sell.

FieldType
idstring (uuid)
vehicle_idstring (uuid)
liquidacion_idstring (uuid)
amount_venta_clpinteger (whole CLP)
comision_amount_clpinteger (whole CLP)
monto_owner_clpinteger (whole CLP)
{
  "id": "d7d7d7d7-0000-4000-8000-000000000001",
  "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
  "liquidacion_id": "d8d8d8d8-0000-4000-8000-000000000001",
  "amount_venta_clp": 8900000,
  "comision_amount_clp": 890000,
  "monto_owner_clp": 8010000
}

credit_application.recorded

A credit application dossier was recorded.

Version 1 · Resource credit_application · data requires credit_applications:read

Fires when. POST /api/v1/credit-applications.

FieldType
idstring (uuid)
display_idstring (F-…)
vehicle_idstring (uuid)
buyer_contact_idstring (uuid) | null
institution_idstring (uuid)
requested_amount_clpinteger (whole CLP)
stateenum: recorded | submitted | approved | rejected | withdrawn
{
  "id": "d9d9d9d9-0000-4000-8000-000000000001",
  "display_id": "F-12",
  "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
  "buyer_contact_id": "22222222-0000-4000-8000-000000000002",
  "institution_id": "dadadada-0000-4000-8000-000000000001",
  "requested_amount_clp": 9500000,
  "state": "recorded"
}

credit_application.submitted

A credit application’s carpeta was recorded as sent to the lender.

Version 1 · Resource credit_application · data requires credit_applications:read

Fires when. POST /api/v1/credit-applications/{id}/submission.

FieldType
idstring (uuid)
display_idstring (F-…)
submitted_bystring (uuid)
submitted_atstring (ISO 8601)
{
  "id": "d9d9d9d9-0000-4000-8000-000000000001",
  "display_id": "F-12",
  "submitted_by": "11111111-0000-4000-8000-000000000001",
  "submitted_at": "2026-09-10T14:00:00.000Z"
}

credit_application.decided

The lender’s verdict was recorded on a credit application.

Version 1 · Resource credit_application · data requires credit_applications:read

Fires when. POST /api/v1/credit-applications/{id}/decision.

FieldType
idstring (uuid)
display_idstring (F-…)
outcomeenum: approved | rejected
approved_amount_clpinteger (whole CLP) | null
decided_bystring (uuid)
decided_atstring (ISO 8601)
{
  "id": "d9d9d9d9-0000-4000-8000-000000000001",
  "display_id": "F-12",
  "outcome": "approved",
  "approved_amount_clp": 9200000,
  "decided_by": "11111111-0000-4000-8000-000000000001",
  "decided_at": "2026-09-16T10:00:00.000Z"
}

credit_application.withdrawn

A credit application was pulled by the dealership — terminal.

Version 1 · Resource credit_application · data requires credit_applications:read

Fires when. POST /api/v1/credit-applications/{id}/withdrawal.

FieldType
idstring (uuid)
display_idstring (F-…)
withdrawn_reasonstring
withdrawn_bystring (uuid)
withdrawn_atstring (ISO 8601)
{
  "id": "d9d9d9d9-0000-4000-8000-000000000001",
  "display_id": "F-12",
  "withdrawn_reason": "el cliente obtuvo mejores condiciones en otra financiera",
  "withdrawn_by": "11111111-0000-4000-8000-000000000001",
  "withdrawn_at": "2026-09-18T09:00:00.000Z"
}

price_approval.requested

Authority over a below-reference price was requested on a quote, a reservation or a nota de venta.

Version 1 · Resource price_approval · data requires price_approval:read

Fires when. POST /api/v1/price-approvals.

FieldType
idstring (uuid)
quote_idstring (uuid) | null
reservation_idstring (uuid) | null
sale_note_idstring (uuid) | null
requested_price_clpinteger (whole CLP)
requester_user_idstring (uuid)
statusenum: pending | approved | rejected
{
  "id": "dbdbdbdb-0000-4000-8000-000000000001",
  "quote_id": null,
  "reservation_id": null,
  "sale_note_id": "d4d4d4d4-0000-4000-8000-000000000001",
  "requested_price_clp": 10900000,
  "requester_user_id": "11111111-0000-4000-8000-000000000002",
  "status": "pending"
}

price_approval.decided

A price approval request was granted or rejected. Terminal — no second decision.

Version 1 · Resource price_approval · data requires price_approval:read

Fires when. POST /api/v1/price-approvals/{id}/decision.

FieldType
idstring (uuid)
statusenum: approved | rejected
decision_notestring | null
decider_user_idstring (uuid)
decided_atstring (ISO 8601)
{
  "id": "dbdbdbdb-0000-4000-8000-000000000001",
  "status": "approved",
  "decision_note": "autorizado por gerencia dado el tiempo en stock",
  "decider_user_id": "11111111-0000-4000-8000-000000000001",
  "decided_at": "2026-09-19T16:05:00.000Z"
}

conversation.created

A new conversation was opened — a customer wrote in on a channel, or the workspace opened the thread from its side.

Version 1 · Resource conversation · data requires conversations:read

Fires when. The first inbound message of a thread (any channel), POST /conversations, or a proactive outbound conversation (ADR 0108). NOT on a historical backfill: importing a dealer’s past threads is inert by design, so a coexistence or history sync fires nothing.

FieldType
conversation_idstring (uuid)
display_idstring — the ID visible, e.g. C-3052
channelstring
contact_idstring (uuid) | null
messaging_account_idstring (uuid) | null
ticket_idstring (uuid) | null
handlerenum: bot | human | external
statusenum: open | pending | snoozed | resolved | closed
sourcestring | null — the portal or campaign it came from
created_atISO 8601 timestamp
{
  "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "display_id": "C-3052",
  "channel": "whatsapp",
  "contact_id": "22222222-0000-4000-8000-000000000001",
  "messaging_account_id": "16161616-0000-4000-8000-000000000001",
  "ticket_id": null,
  "handler": "bot",
  "status": "open",
  "source": null,
  "created_at": "2026-09-22T11:06:33.744Z"
}

conversation.assigned

A conversation changed hands: a new assignee, a new handler, or both.

Version 1 · Resource conversation · data requires conversations:read

Fires when. POST /conversations/:id/assign, /claim, /handoff or /return-to-ai, the assignment rules routing an unowned inbound, and a ticket assignment mirroring onto its conversation. NOT on a no-op (the same assignee and the same handler).

FieldType
conversation_idstring (uuid)
display_idstring — the ID visible, e.g. C-3052
from_assignee_user_idstring (uuid) | null
to_assignee_user_idstring (uuid) | null
from_handlerenum: bot | human | external | null
to_handlerenum: bot | human | external
ticket_idstring (uuid) | null
{
  "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "display_id": "C-3052",
  "from_assignee_user_id": null,
  "to_assignee_user_id": "11111111-0000-4000-8000-000000000001",
  "from_handler": "bot",
  "to_handler": "human",
  "ticket_id": "dddddddd-0000-4000-8000-000000000001"
}

conversation.resolved

A conversation stopped being open work. changes.status.to is resolved (dealt with, reopened by the next inbound message) or closed (terminal).

Version 1 · Resource conversation · data requires conversations:read

Fires when. POST /conversations/:id/resolve, POST /conversations/:id/close on a thread that was not already resolved, and the auto-resolve worker. The auto-CLOSE of an already-resolved thread fires nothing: the conclusion was already reported, and reporting the grace window as a second conclusion is the two-events-for-one-action mistake. A ticket.resolved / ticket.closed follows only when no sibling conversation on that ticket is still active.

FieldType
conversation_idstring (uuid)
display_idstring — the ID visible, e.g. C-3052
from_statusenum: open | pending | snoozed | resolved
to_statusenum: resolved | closed
resolved_byenum: bot | human | system | null
resolved_atISO 8601 timestamp | null
ticket_idstring (uuid) | null
{
  "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "display_id": "C-3052",
  "from_status": "open",
  "to_status": "resolved",
  "resolved_by": "human",
  "resolved_at": "2026-09-22T11:15:06.653Z",
  "ticket_id": "dddddddd-0000-4000-8000-000000000001"
}

message.received

The customer sent a message.

Version 1 · Resource message · data requires conversations:read

Fires when. A customer message is persisted on any channel. NOT on a history backfill (a coexistence or Instagram history sync writes customer messages with the fan-out switched off, so importing months of threads does not replay months of events), and not for the internal system lines the thread carries for assignments and status changes.

FieldType
message_idstring (uuid)
conversation_idstring (uuid)
contact_idstring (uuid) | null
channelstring
typeenum: text | image | audio | video | file | location | …
contentstring — empty on a media-only message
external_message_idstring | null — the provider’s id, not a Vitrina reference
created_atISO 8601 timestamp
{
  "message_id": "eeeeeeee-0000-4000-8000-000000000001",
  "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "contact_id": "22222222-0000-4000-8000-000000000001",
  "channel": "whatsapp",
  "type": "text",
  "content": "¿Me pueden confirmar la hora del jueves?",
  "external_message_id": "wamid.HBgLNTY5NzcwMDAwMjE",
  "created_at": "2026-09-22T11:07:02.824Z"
}

message.sent

The workspace sent a message to the customer. The envelope’s author says who wrote it: a member, a member through a connected app or a personal token, an API key, or the AI agent.

Version 1 · Resource message · data requires conversations:read

Fires when. An outbound message is persisted — the AI answering, a teammate replying, an API send, a template, a flow, a location pin, an attachment or a voice note. It fires at PERSIST, which on most channels is the moment Vitrina commits to the send rather than the moment the provider confirms it: delivery_status carries what was known then, and a later failure or delivery receipt shows on the message row (GET /conversations/{id}/messages), not as a second event. Internal system lines and internal notes are not messages and fire nothing.

FieldType
message_idstring (uuid)
conversation_idstring (uuid)
contact_idstring (uuid) | null
channelstring
typeenum: text | image | audio | video | file | location | …
contentstring
sender_typeenum: ai_agent | human_user | api_key
delivery_statusenum: sent | delivered | read | retrying | failed | null
created_atISO 8601 timestamp
{
  "message_id": "eeeeeeee-0000-4000-8000-000000000002",
  "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "contact_id": "22222222-0000-4000-8000-000000000001",
  "channel": "whatsapp",
  "type": "text",
  "content": "Hola Rodrigo, te confirmo la visita del jueves a las 10:00.",
  "sender_type": "api_key",
  "delivery_status": "sent",
  "created_at": "2026-09-22T11:08:10.882Z"
}

ticket.opened

A new ticket has been opened on a conversation.

Version 1 · Resource ticket · data requires tickets:read

Fires when. POST /conversations/:id/tickets, the open_ticket / handoff tools, or a stale handoff being reopened with a brand-new ticket.

FieldType
ticket_idstring (uuid)
origin_conversation_idstring (uuid) | null
reasonstring | null
opened_byenum: bot | human | system | rule
opened_viastring | null
brandstring | null
{
  "ticket_id": "dddddddd-0000-4000-8000-000000000001",
  "origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "reason": "El cliente pide reagendar su visita",
  "opened_by": "human",
  "opened_via": "admin_ui",
  "brand": null
}

ticket.reopened

A previously resolved or closed ticket has been reopened.

Version 1 · Resource ticket · data requires tickets:read

Fires when. reopenTicket() in the ticket service — typically triggered by new inbound activity on a stale handoff.

FieldType
ticket_idstring (uuid)
origin_conversation_idstring (uuid) | null
reasonstring | null
brandstring | null
{
  "ticket_id": "dddddddd-0000-4000-8000-000000000001",
  "origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "reason": "customer_replied",
  "brand": null
}

ticket.resolved

A ticket has transitioned to status=resolved.

Version 1 · Resource ticket · data requires tickets:read

Fires when. PUT /tickets/:id/close (which resolves, despite the path), the auto-resolve worker, and a conversation resolve cascading to its ticket when no sibling conversation is still active. NOT on PUT /tickets/:id/status with status=resolved — that route sets the column without stamping resolved_by/resolved_at, so it has never emitted this.

FieldType
ticket_idstring (uuid)
origin_conversation_idstring (uuid) | null
resolved_bystring
resolved_atISO 8601 timestamp
brandstring | null
{
  "ticket_id": "dddddddd-0000-4000-8000-000000000001",
  "origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "resolved_by": "human",
  "resolved_at": "2026-09-22T11:15:06.653Z",
  "brand": null
}

ticket.closed

A ticket has transitioned to status=closed (terminal).

Version 1 · Resource ticket · data requires tickets:read

Fires when. A conversation is closed (auto-close worker after the grace window, or a manual close) and no other open conversation holds the ticket.

FieldType
ticket_idstring (uuid)
origin_conversation_idstring (uuid) | null
closed_bystring
brandstring | null
{
  "ticket_id": "dddddddd-0000-4000-8000-000000000001",
  "origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
  "closed_by": "auto",
  "brand": null
}

sla.breached

An open ticket crossed an applicable SLA policy target (first response or resolution). Fired once per (ticket, policy, breach type).

Version 1 · Resource ticket · data requires tickets:read

Fires when. The SLA monitor worker (10-min tick) detects a newly-breached open ticket. Business hours are honoured when the policy is business_hours_only.

FieldType
ticket_idstring (uuid)
ticket_display_idstring | null (e.g. T-1247)
policy_idstring (uuid)
policy_namestring
breach_typeenum: first_response | resolution
target_minutesinteger
elapsed_secondsinteger
assigneestring | null (agent display name)
breached_atISO 8601 timestamp
{
  "ticket_id": "9b1c…",
  "ticket_display_id": "T-1247",
  "policy_id": "ec95…",
  "policy_name": "Soporte estándar",
  "breach_type": "resolution",
  "target_minutes": 1440,
  "elapsed_seconds": 95400,
  "assignee": "Paulina Rojas",
  "breached_at": "2026-06-07T18:20:00.000Z"
}

agent_scenario_run.completed

An Agent Evals scenario run reached a terminal VERDICT (passed | partial | failed | error) — ADR 0098. A cancelled run does not fire it: the configuration moved under the run (or a human stopped it), so there is no result to report and a mass cancellation would otherwise be one webhook per run.

Version 1 · Resource agent_scenario_run · data requires ai_agents:read

Fires when. The scenario-run worker finishes a run queued by POST /ai-agents/:id/scenarios/run, a single-scenario run, or a publish.

FieldType
run_idstring (uuid)
scenario_idstring (uuid)
scenario_display_idstring (EV-n)
ai_agent_idstring (uuid)
agent_version_kind"draft" | "live"
batch_idstring (uuid) | null
status"passed" | "partial" | "failed" | "error"
scorenumber 0-100 | null
hard_fails[{ id, kind, evidence }]
duration_msnumber
{
  "run_id": "3c1e2d4f-…",
  "scenario_id": "9a7b6c5d-…",
  "scenario_display_id": "EV-12",
  "ai_agent_id": "7c2e3a0e-…",
  "agent_version_kind": "draft",
  "batch_id": "b1a2c3d4-…",
  "status": "failed",
  "score": 0,
  "hard_fails": [
    {
      "id": "grounded-3",
      "kind": "grounded",
      "evidence": "turn 1: not in any tool result → 10:20"
    }
  ],
  "duration_ms": 48210
}

agent_scenario_suite.completed

An Agent Evals SUITE run finished — every scenario run in its batch reached a terminal status (ADR 0098 phase 1B).

Version 1 · Resource agent_scenario_suite_run · data requires ai_agents:read

Fires when. The last run of the suite's batch finishes, whatever started it: POST /ai-agents/:id/scenario-suites/:suiteId/run, the nightly cron, or a publish of an agent that has a golden suite. Exactly once per suite run — the completion flip is a conditional update.

FieldType
suite_run_idstring (uuid)
suite_idstring (uuid)
suite_namestring | null
kindenum: golden|nightly|exploratory|on_change (null if the suite was deleted mid-run)
ai_agent_idstring (uuid)
batch_idstring (uuid) — the agent_scenario_run.batch_id
triggerenum: manual|publish|cron|api
agent_version_kind"draft" | "live"
agent_version_numbernumber | null
summary{ n, passed, partial, failed, error, hard_fails, pass_rate (0-100 | null), flaky, cost_usd, duration_ms }
delta{ pass_rate: number | null, vs_suite_run_id: string | null } | null
{
  "suite_run_id": "5f6e7d8c-…",
  "suite_id": "1a2b3c4d-…",
  "suite_name": "Golden",
  "kind": "golden",
  "ai_agent_id": "7c2e3a0e-…",
  "batch_id": "b1a2c3d4-…",
  "trigger": "cron",
  "agent_version_kind": "live",
  "agent_version_number": 41,
  "summary": {
    "n": 66,
    "passed": 54,
    "partial": 7,
    "failed": 4,
    "error": 1,
    "hard_fails": 4,
    "pass_rate": 83.1,
    "flaky": 2,
    "cost_usd": 1.842,
    "duration_ms": 412000
  },
  "delta": {
    "pass_rate": -3.1,
    "vs_suite_run_id": "4e5d6c7b-…"
  }
}

ai_agent.publish

An AI agent draft has been published as the live config.

Version 1 · Resource ai_agent · data requires ai_agents:read

Fires when. POST /ai-agents/:id/publish.

FieldType
ai_agent_idstring (uuid)
namestring | null
updated_atISO 8601 timestamp | null
{
  "ai_agent_id": "7c2e3a0e-…",
  "name": "Soporte Vitrina",
  "updated_at": "2026-05-15T03:47:40.357Z"
}

ai_agent_graph.publish

A multi-agent graph was published — the draft topology became the live one, snapshotted as a new immutable version.

Version 1 · Resource ai_agent_graph · data requires ai_agents:read

Fires when. The graph publish seam validates the draft and inserts the version row (ai-agent-graph.service). The eval cascade that may follow does not gate the event.

FieldType
tenant_idstring (uuid)
graph_idstring (uuid)
version_idstring (uuid) — the snapshot that just went live
version_numberinteger — 1 for the first publish, then monotonic
topologyenum: single | sequential | routing | supervisor | parallel | evaluator_loop
{
  "tenant_id": "00000000-0000-4000-8000-000000000001",
  "graph_id": "9f1c6b40-3a2e-4f58-9d17-70c4f1a2b3d5",
  "version_id": "d0a7e912-5c84-4b6f-8e1a-2f9c7b3d0e45",
  "version_number": 7,
  "topology": "supervisor"
}

cost.budget_alert

An AI spend threshold was crossed. ⚠ ONE NAME, THREE PRODUCERS, THREE SHAPES — branch on the keys you get, not on a single schema. The Copilot credit gate emits domain: "copilot" with the credit counters; the Copilot dollar-margin guard emits domain: "copilot" with the USD limits; the graph runtime budget emits tenant_id + pct and no domain at all. Every field below is therefore optional in practice.

Version 1 · Resource workspace · data requires billing:read

Fires when. A tenant exhausts its monthly Copilot credit allowance, crosses its soft or hard USD margin limit (once each per month — the row carries the dedupe stamp), or crosses 50%, 80% or 100% of its monthly graph-runtime cost budget (once per threshold per month).

FieldType
domainenum: copilot — present only on the two Copilot producers; absent on the graph-runtime one
monthstring (YYYY-MM) — Copilot producers only
severityenum: credits_exhausted | soft | hard — Copilot producers only
credit_allowanceinteger — credits granted this month; credits_exhausted only
credits_usedinteger — credits consumed; credits_exhausted only
grace_creditsinteger — credits served past the allowance; credits_exhausted only
used_usdnumber — margin spent this month; Copilot producers only
soft_limit_usdnumber — soft/hard severities only
hard_limit_usdnumber — soft/hard severities only
tenant_idstring (uuid) — graph-runtime producer only
pctinteger: 50 | 80 | 100 — the threshold crossed; graph-runtime producer only
budget_usdnumber — the monthly budget; graph-runtime producer only
spent_usdnumber — spent so far this month; graph-runtime producer only
{
  "domain": "copilot",
  "month": "2026-09",
  "severity": "soft",
  "used_usd": 41.28,
  "soft_limit_usd": 40,
  "hard_limit_usd": 60
}

runtime.budget_exceeded

A multi-agent graph turn hit one of its runtime ceilings and was cut short. The turn still answers; what stopped is the orchestration, so a graph firing this repeatedly is mis-shaped rather than merely busy.

Version 1 · Resource ai_agent_graph · data requires ai_agents:read

Fires when. The graph runtime refuses to go further on one of four ceilings: recursion depth, fan-out width, per-node turns, or evaluator-loop iterations.

FieldType
tenant_idstring (uuid)
graph_idstring (uuid)
topologyenum: single | sequential | routing | supervisor | parallel | evaluator_loop
reasonenum: depth | fan_out | node_turns | evaluator_iterations
{
  "tenant_id": "00000000-0000-4000-8000-000000000001",
  "graph_id": "9f1c6b40-3a2e-4f58-9d17-70c4f1a2b3d5",
  "topology": "evaluator_loop",
  "reason": "evaluator_iterations"
}

kb.source.create

A new knowledge-base source has been added to the workspace.

Version 1 · Resource kb_source · data requires kb:read

Fires when. POST /kb/sources.

FieldType
kb_source_idstring (uuid)
titlestring | null
tagsstring[] | null
{
  "kb_source_id": "b2…",
  "title": "Devoluciones — política",
  "tags": [
    "returns",
    "policy"
  ]
}

kb.source.embed

A KB source has been (re-)embedded; vectors are ready for retrieval.

Version 1 · Resource kb_source · data requires kb:read

Fires when. POST /kb/sources/:id/embed (manual) or the kb-ingestion worker after content changes.

FieldType
kb_source_idstring (uuid)
chunksinteger
{
  "kb_source_id": "b2…",
  "chunks": 47
}

kb.source.update

A knowledge-base source’s summary, tags, status or verified_until changed.

Version 1 · Resource kb_source · data requires kb:read

Fires when. PUT /kb/sources/:id.

FieldType
kb_source_idstring (uuid)
fieldsstring[] — the body keys that were sent
{
  "kb_source_id": "a5a5a5a5-0000-4000-8000-000000000001",
  "fields": [
    "status"
  ]
}

kb.source.delete

A knowledge-base source was HARD-deleted — the row and its embedded chunks are gone; retrieval can no longer return it.

Version 1 · Resource kb_source · data requires kb:read

Fires when. DELETE /kb/sources/:id.

FieldType
kb_source_idstring (uuid)
{
  "kb_source_id": "a5a5a5a5-0000-4000-8000-000000000001"
}

kb_file.uploaded

A document was added to the workspace’s knowledge file library. A 201 here means the bytes are stored, not that the file is searchable yet — ingestion runs best-effort on a worker.

Version 1 · Resource kb_file · data requires kb:read

Fires when. POST /kb-files (multipart upload).

FieldType
kb_file_idstring (uuid)
namestring
content_typestring
{
  "kb_file_id": "a6a6a6a6-0000-4000-8000-000000000001",
  "name": "Garantías 2026.pdf",
  "content_type": "application/pdf"
}

kb_file.deleted

A knowledge file was removed from the workspace (ADR 0093 — a soft delete: the row survives tombstoned, but every agent attachment goes and the embedded chunks are purged, so this is the customer-visible ending).

Version 1 · Resource kb_file · data requires kb:read

Fires when. DELETE /kb-files/:id.

FieldType
kb_file_idstring (uuid)
{
  "kb_file_id": "a6a6a6a6-0000-4000-8000-000000000001"
}

skill.created

A new skill was added to the tenant library.

Version 1 · Resource skill · data requires ai_agents:read

Fires when. POST /skills.

FieldType
skill_idstring (uuid)
namestring
{
  "skill_id": "a4a4a4a4-0000-4000-8000-000000000001",
  "name": "Agendar hora"
}

skill.updated

A skill’s content or channel_overrides changed — applies instantly to every agent it is attached to; there is no draft/publish step for a skill.

Version 1 · Resource skill · data requires ai_agents:read

Fires when. PUT /skills/:id.

FieldType
skill_idstring (uuid)
namestring
{
  "skill_id": "a4a4a4a4-0000-4000-8000-000000000001",
  "name": "Agendar hora"
}

skill.deleted

A skill was removed (ADR 0093 — soft delete): it leaves the library and every agent it was attached to immediately.

Version 1 · Resource skill · data requires ai_agents:read

Fires when. DELETE /skills/:id.

FieldType
skill_idstring (uuid)
{
  "skill_id": "a4a4a4a4-0000-4000-8000-000000000001"
}

custom_tool.created

A new custom tool was added to the Tool Store.

Version 1 · Resource custom_tool · data requires functions:read

Fires when. POST /custom-tools.

FieldType
custom_tool_idstring (uuid)
namestring
{
  "custom_tool_id": "a7a7a7a7-0000-4000-8000-000000000001",
  "name": "consultar_clima"
}

custom_tool.updated

A custom tool’s definition changed — its request template, parameters, auth configuration or enabled state. Fires from both the full-replace (PUT) and partial (PATCH) routes.

Version 1 · Resource custom_tool · data requires functions:read

Fires when. PUT or PATCH /custom-tools/:id.

FieldType
custom_tool_idstring (uuid)
namestring
{
  "custom_tool_id": "a7a7a7a7-0000-4000-8000-000000000001",
  "name": "consultar_clima"
}

custom_tool.deleted

A custom tool was removed. Every agent wired to it loses it immediately; past tool_invocation audit rows survive.

Version 1 · Resource custom_tool · data requires functions:read

Fires when. DELETE /custom-tools/:id.

FieldType
custom_tool_idstring (uuid)
{
  "custom_tool_id": "a7a7a7a7-0000-4000-8000-000000000001"
}

lead.created

A new lead has been opened — an opportunity the workspace is now working: a buyer asking about a car, a patient asking for a treatment, a company asking for a quote.

Version 1 · Resource lead · data requires leads:read

Fires when. POST /leads, POST /leads/from-conversation, POST /leads/import, the create_lead AI tool, or a marketplace / dealer-website ingest.

FieldType
lead_idstring (uuid)
contact_idstring (uuid)
pipeline_idstring (uuid)
stage_idstring (uuid)
sourceenum: conversation | marketplace | manual | import | ai_agent | website | chileautos | yapo | mercadolibre | facebook_marketplace
intentenum: buy | sell | financing | trade_in (defaults to buy)
titlestring
value_amountnumber | null
value_currencystring | null
owner_user_idstring (uuid) | null
team_idstring (uuid) | null
{
  "lead_id": "b9e4a1c2-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "contact_id": "22222222-0000-4000-8000-000000000001",
  "pipeline_id": "44444444-0000-4000-8000-000000000001",
  "stage_id": "55555555-0000-4000-8000-000000000001",
  "source": "conversation",
  "intent": "buy",
  "title": "María — prueba de manejo Corolla 2022",
  "value_amount": 8990000,
  "value_currency": "CLP",
  "owner_user_id": "3897d483-9e84-4971-8b17-9a41a6811966",
  "team_id": null
}

lead.stage_changed

A lead moved to a different stage of its board. EVERY stage change fires this — the REST move, a drag on the board, an AI tool, a quick action that closes the lead, the lead reviewer — so a receiver that tracks progress needs only this one event.

Version 1 · Resource lead · data requires leads:read

Fires when. PUT /leads/:id/stage, PUT /leads/:id/won | /lost | /unqualify | /reopen, the update_lead_stage AI tool, or the lead reviewer moving a card. NOT on a no-op move (already on that stage), and not on PUT /leads/:id/pipeline — that one reports its own stage move inside lead.pipeline_changed.

FieldType
lead_idstring (uuid)
from_stage_idstring (uuid) | null
to_stage_idstring (uuid)
to_stage_slugstring | null
reasonstring | null
{
  "lead_id": "b9e4a1c2-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "from_stage_id": "55555555-0000-4000-8000-000000000001",
  "to_stage_id": "55555555-0000-4000-8000-000000000002",
  "to_stage_slug": "contacted",
  "reason": "la llamamos y confirmó la hora"
}

lead.pipeline_changed

A lead was reclassified onto a different sales board, landing on that board’s first stage.

Version 1 · Resource lead · data requires leads:read

Fires when. PUT /leads/:id/pipeline or the update_lead_pipeline AI tool. No separate lead.stage_changed fires for the landing stage — this event already carries it.

FieldType
lead_idstring (uuid)
from_pipeline_idstring (uuid) | null
to_pipeline_idstring (uuid)
from_stage_idstring (uuid) | null
to_stage_idstring (uuid)
reasonstring | null
{
  "lead_id": "b9e4a1c2-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "from_pipeline_id": "44444444-0000-4000-8000-000000000001",
  "to_pipeline_id": "44444444-0000-4000-8000-000000000002",
  "from_stage_id": "55555555-0000-4000-8000-000000000002",
  "to_stage_id": "55555555-0000-4000-8000-000000000003",
  "reason": "es una venta mayorista"
}

lead.assigned

A lead was (re)assigned to an owner or a team. One assignee owns the whole relationship, so the lead’s conversations and their tickets move with it.

Version 1 · Resource lead · data requires leads:read

Fires when. PUT /leads/:id/assign, PUT /leads/:id with owner_user_id, or the team rotation picking an owner on create.

FieldType
lead_idstring (uuid)
owner_user_idstring (uuid) | null
team_idstring (uuid) | null
{
  "lead_id": "b9e4a1c2-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "owner_user_id": "3897d483-9e84-4971-8b17-9a41a6811966",
  "team_id": "cccccccc-0000-4000-8000-000000000001"
}

lead.won

A lead closed in the workspace’s favour.

Version 1 · Resource lead · data requires leads:read

Fires when. PUT /leads/:id/won, or any move onto a stage whose category is won. Only on the STATUS FLIP: moving an already-won lead between two won columns is a board tidy-up, not a second sale.

FieldType
lead_idstring (uuid)
reasonstring | null
value_amountnumber | null
value_currencystring | null
{
  "lead_id": "b9e4a1c2-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "reason": "cerró con financiamiento propio",
  "value_amount": 8750000,
  "value_currency": "CLP"
}

lead.lost

A real opportunity did not close.

Version 1 · Resource lead · data requires leads:read

Fires when. PUT /leads/:id/lost, or any move onto a stage whose category is lost. Status flip only, as for lead.won.

FieldType
lead_idstring (uuid)
reasonstring | null
value_amountnumber | null
value_currencystring | null
{
  "lead_id": "b9e4a1c2-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "reason": "compró en otra parte",
  "value_amount": 8990000,
  "value_currency": "CLP"
}

lead.unqualified

There was never an opportunity here — the wrong product, the wrong workspace, a duplicate, a test. A DISTINCT outcome from lead.lost: unqualified leads are excluded from win rate on both sides, so qualifying early never looks like losing.

Version 1 · Resource lead · data requires leads:read

Fires when. PUT /leads/:id/unqualify, or any move onto a stage whose category is unqualified. Status flip only, as for lead.won.

FieldType
lead_idstring (uuid)
reasonstring | null
value_amountnumber | null
value_currencystring | null
{
  "lead_id": "b9e4a1c2-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "reason": "buscaba un servicio que no ofrecemos",
  "value_amount": null,
  "value_currency": "CLP"
}

macro.applied

A macro has been applied to a conversation AND the macro opts into the webhook notification (i.e. its action list includes a run_webhook step). Lean payload — consumers use the ids to fetch detail if they need it.

Version 1 · Resource conversation · data requires conversations:read

Fires when. POST /macros/:id/apply with a conversation_id, when the macro's actions array contains at least one step with type=run_webhook. Fires once per apply regardless of how many run_webhook steps the macro has.

FieldType
macro_idstring (uuid)
macro_namestring
conversation_idinteger
ticket_idinteger | null
contact_idinteger | null
executed_stepsarray of {type: string, ok: boolean, error: string | null}
applied_by_user_idstring (uuid) | null
applied_atISO 8601 timestamp
{
  "macro_id": "8f74aa7c-df40-478d-b5f5-5cfc2eb98da4",
  "macro_name": "Send return label",
  "conversation_id": 10031,
  "ticket_id": 10042,
  "contact_id": 142,
  "executed_steps": [
    {
      "type": "send_message",
      "ok": true
    },
    {
      "type": "add_label",
      "ok": true
    },
    {
      "type": "change_status",
      "ok": true
    },
    {
      "type": "run_webhook",
      "ok": true
    }
  ],
  "applied_by_user_id": "3897d483-9e84-4971-8b17-9a41a6811966",
  "applied_at": "2026-05-15T03:47:40.357Z"
}

stock.source_retired

Retiro de fuente (ADR 0031): a decommissioned import source handed its vehicles to dealer ownership.

Version 1 · Resource marketplace_integration · data requires marketplace:read

Fires when. POST /api/v1/vehicles/source-retirement/execute completes (once per retired source).

FieldType
integration_idstring (uuid)
providerstring
retiredinteger
revivedinteger
kept_hiddeninteger
{
  "integration_id": "a3f1…",
  "provider": "veekls",
  "retired": 156,
  "revived": 2,
  "kept_hidden": 3
}

stock.synced

A stock integration finished importing active inventory from a marketplace portal.

Version 1 · Resource marketplace_integration · data requires marketplace:read

Fires when. syncStock() completes successfully (manual POST /marketplaces/:id/sync or the scheduled tick).

FieldType
integration_idstring (uuid)
providerstring
vehicles_importedinteger
vehicles_updatedinteger
vehicles_deactivatedinteger
{
  "integration_id": "a3f1…",
  "provider": "chileautos",
  "vehicles_imported": 12,
  "vehicles_updated": 38,
  "vehicles_deactivated": 3
}

vehicle.published

A vehicle was published (or re-published) to a marketplace portal.

Version 1 · Resource vehicle · data requires stock:read

Fires when. publishVehicle() succeeds for a portal via POST /vehicles/:id/publish.

FieldType
vehicle_idstring (uuid)
integration_idstring (uuid)
providerstring
external_idstring
{
  "vehicle_id": "c8d2…",
  "integration_id": "a3f1…",
  "provider": "chileautos",
  "external_id": "6f2b1f7e-1f2a-4c3d-9e8f-0a1b2c3d4e5f"
}

vehicle.unpublished

A vehicle’s publication was removed from a marketplace portal — either a synchronous unpublish or a Facebook Marketplace device-agent task confirming a remove it had only enqueued.

Version 1 · Resource vehicle · data requires stock:read

Fires when. unpublishVehicle() succeeds via DELETE /vehicles/{id}/publications/{pubId}, or the device agent reports a remove task done via POST /device-agent/tasks/{id}/result (facebook_marketplace — the enqueue itself fires nothing; only the confirmed removal does).

FieldType
vehicle_idstring (uuid)
integration_idstring (uuid)
providerstring
external_idstring | null — the portal’s ad id, when one was ever assigned
{
  "vehicle_id": "c8d2…",
  "integration_id": "a3f1…",
  "provider": "mercadolibre",
  "external_id": "MLC-1234567890"
}

vehicle.publish_failed

A publish, update or unpublish attempt against a marketplace portal failed — the portal rejected it, the connection errored, or (Facebook Marketplace) the device agent reported the task could not complete.

Version 1 · Resource vehicle · data requires stock:read

Fires when. The per-integration catch in publishVehicle() via POST /vehicles/{id}/publish, or the device agent reporting a publish/update/remove task failed via POST /device-agent/tasks/{id}/result.

FieldType
vehicle_idstring (uuid)
integration_idstring (uuid)
providerstring
errorstring
{
  "vehicle_id": "c8d2…",
  "integration_id": "a3f1…",
  "provider": "chileautos",
  "error": "The portal rejected the listing: missing required field \"version\"."
}

vehicle.created

A vehicle entered this workspace — one event per car, whichever door it came through: the manual/API create (POST /vehicles) or a marketplace stock import. Use it to mirror the lot: id is the handle every other endpoint takes, and url fetches the full record. A car that is MERGED into one you already know about does not fire this event (the identity resolver attached the new listing to the existing vehicle, so no car appeared); neither does a re-import of a car already in stock.

Version 1 · Resource vehicle · data requires stock:read

Fires when. A vehicle row is inserted — vehicle.service create (manual/API intake) or either insert door of the marketplace-sync importer (a newcomer, and a newcomer whose plate conflicted and was imported plate-absent). Retries of one delivery reuse the event id.

FieldType
idstring (uuid) — pass to GET /api/v1/vehicles/{id}
patentestring | null — Chilean plate as entered; null when absent
vinstring (17 chars) | null
makestring | null
modelstring | null
versionstring | null
yearinteger | null
kminteger | null — kilometres (a miles odometer is converted)
priceinteger | null — whole CLP pesos; null = no price recorded, 0 = free
statusenum: disponible | reservado | vendido
tenenciaenum: propio | consignacion
locationobject { id: uuid, name: string } | null — the branch, null when untagged
sourcestring — manual, or the portal the import came from (chileautos, mercadolibre, yapo, …)
created_atstring (ISO 8601)
urlstring (absolute URL) | null — this vehicle on the Vitrina API; null only on a deployment with no public API base configured
{
  "id": "c8d24f0e-7a1b-4c3d-9e8f-0a1b2c3d4e5f",
  "patente": "KXRT52",
  "vin": "JTDBR32E730123456",
  "make": "Toyota",
  "model": "Yaris",
  "version": "XLS 1.5 AT",
  "year": 2019,
  "km": 48200,
  "price": 8990000,
  "status": "disponible",
  "tenencia": "propio",
  "location": {
    "id": "25424bd5-57e1-412c-a0fe-5c375608cef7",
    "name": "Manquehue"
  },
  "source": "chileautos",
  "created_at": "2026-09-15T13:22:41.118Z",
  "url": "https://api.vitrinadev.com/api/v1/vehicles/c8d24f0e-7a1b-4c3d-9e8f-0a1b2c3d4e5f"
}

vehicle.attachment.created

A document was filed into a vehicle's expediente — padrón, cédula del consignante, contrato firmado, certificado de anotaciones vigentes or factura. The event carries METADATA AND A FETCH URL, never file bytes and never a signed URL: GET content_url with an API key holding vehicle_registry:read and the bytes stream back (every read is audited). ⚠ PERSONAL DATA: filename is chosen by the person who uploaded the file and regularly carries a natural person's name or RUT, and a cedula is a third party's identity document. Receiving these makes your platform a PROCESSOR of the dealership's personal data under Ley 21.719 — store them under the dealership's retention rules, keep them out of logs, and be able to erase them on request.

Version 1 · Resource vehicle_attachment · data requires vehicle_registry:read

Fires when. The expediente upload seam persists the row (POST /vehicle-attachments, the only upload door — a transferencia references an already-filed document rather than uploading its own). Retries of one delivery reuse the event id.

FieldType
idstring (uuid) — the attachment
vehicle_idstring (uuid) — the car this expediente belongs to
kindenum: padron | cedula | contrato | certificado_anotaciones | factura | otro
filenamestring — user-chosen; may carry a person’s name (see description)
mime_typestring — application/pdf, image/jpeg, image/png, image/webp
byte_sizeinteger
uploaded_atstring (ISO 8601)
content_urlstring (absolute URL) | null — streams the bytes to a key holding vehicle_registry:read; 403 without it. Not signed and not public: authenticate the request. Null only on a deployment with no public API base configured.
{
  "id": "3f9c1a52-64bd-4a71-8e2c-9b7d5e3a1c04",
  "vehicle_id": "c8d24f0e-7a1b-4c3d-9e8f-0a1b2c3d4e5f",
  "kind": "padron",
  "filename": "padron_KXRT52.pdf",
  "mime_type": "application/pdf",
  "byte_size": 184320,
  "uploaded_at": "2026-09-15T13:31:08.402Z",
  "content_url": "https://api.vitrinadev.com/api/v1/vehicle-attachments/3f9c1a52-64bd-4a71-8e2c-9b7d5e3a1c04/content"
}

mercadolibre.order

A Mercado Libre order notification (orders_v2) was received for a connected seller.

Version 1 · Resource marketplace_integration · data requires marketplace:read

Fires when. The MELI notifications worker processes an orders_v2 topic. It also upserts the buyer as a contact and creates a sales lead (source=marketplace, deduped by order id) when the tenant has a sales pipeline; this event fires regardless for downstream automation/reporting.

FieldType
integration_idstring (uuid)
order_idnumber
statusstring
total_amountnumber
currency_idstring
buyerobject
itemsarray
{
  "integration_id": "a3f1…",
  "order_id": 2000003508419013,
  "status": "paid",
  "total_amount": 8990000,
  "currency_id": "CLP",
  "buyer": {
    "id": 123456789,
    "nickname": "COMPRADOR123"
  },
  "items": [
    {
      "id": "MLC123",
      "title": "Toyota Yaris XLS 2019",
      "qty": 1
    }
  ]
}

mercadolibre.vis_lead

A Mercado Libre vehicle-classified buyer lead (VIS Leads) was received for a connected seller.

Version 1 · Resource marketplace_integration · data requires marketplace:read

Fires when. The MELI notifications worker processes a vis_leads topic (buyer contact via whatsapp/call/question/visit/contact request/reservation). It also upserts the buyer as a contact and creates a sales lead (source=marketplace, deduped by lead id) when the tenant has a sales pipeline; this event fires regardless for downstream automation/reporting.

FieldType
integration_idstring (uuid)
lead_idstring
item_idstring | null
contact_typestring (whatsapp|call|question|visit_request|contact_request|reservation)
statusstring | null
{
  "integration_id": "a3f1…",
  "lead_id": "44115522",
  "item_id": "MLC123",
  "contact_type": "whatsapp",
  "status": "active"
}

appointment.booked

An appointment was booked and confirmed — a test drive, a consulta, a service visit: whatever the workspace puts in its diary.

Version 1 · Resource appointment · data requires appointments:read

Fires when. POST /appointments, the agent booking tool, the embed widget, the clinic diary (POST /clinic/agenda/appointments), or a hold that was confirmed. NOT for kind=block (an admin unavailability marker is not a booking).

FieldType
idstring (uuid)
display_idstring (A-…, the ID visible)
statusenum: pending_hold|confirmed|cancelled|completed|no_show
kindenum: test_drive|external|block|clinic
starts_atstring (ISO 8601)
ends_atstring (ISO 8601)
appointment_type_idstring (uuid) | null
vehicle_idstring (uuid) | null
owner_user_idstring (uuid) | null
location_idstring (uuid) | null
lead_idstring (uuid) | null
contact_idstring (uuid) | null
{
  "id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "display_id": "A-42",
  "status": "confirmed",
  "kind": "clinic",
  "starts_at": "2026-05-28T17:00:00.000Z",
  "ends_at": "2026-05-28T18:00:00.000Z",
  "appointment_type_id": null,
  "vehicle_id": null,
  "owner_user_id": "b1d2c3e4-5f60-4a71-8b92-0c3d4e5f6a70",
  "location_id": "9e8d7c6b-5a49-4382-9271-6f5e4d3c2b10",
  "lead_id": null,
  "contact_id": "5f3a2b1c-0d9e-48f7-a6b5-c4d3e2f10987"
}

appointment.rescheduled

An appointment moved to a new time. changes always carries starts_at and ends_at; it ALSO carries owner and/or location when the same request reassigned the appointment or moved it to another sucursal — one action, one event, so a receiver never has to stitch a move and a reassignment back together.

Version 1 · Resource appointment · data requires appointments:read

Fires when. PATCH /appointments/:id with a new starts_at + ends_at, the agent reschedule tool, or the clinic diary (PATCH /clinic/agenda/appointments/:id). Only for a CONFIRMED appointment: moving a pending hold is not a reschedule anybody has been told about yet. A reassignment or a sucursal move on its own does not fire it — nothing moved in the diary.

FieldType
idstring (uuid)
display_idstring (A-…, the ID visible)
statusenum: pending_hold|confirmed|cancelled|completed|no_show
kindenum: test_drive|external|block|clinic
starts_atstring (ISO 8601)
ends_atstring (ISO 8601)
previous_starts_atstring (ISO 8601)
previous_ends_atstring (ISO 8601)
appointment_type_idstring (uuid) | null
vehicle_idstring (uuid) | null
owner_user_idstring (uuid) | null
location_idstring (uuid) | null
lead_idstring (uuid) | null
contact_idstring (uuid) | null
{
  "id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "display_id": "A-42",
  "status": "confirmed",
  "kind": "clinic",
  "starts_at": "2026-05-29T14:00:00.000Z",
  "ends_at": "2026-05-29T15:00:00.000Z",
  "previous_starts_at": "2026-05-28T17:00:00.000Z",
  "previous_ends_at": "2026-05-28T17:45:00.000Z",
  "appointment_type_id": null,
  "vehicle_id": null,
  "owner_user_id": "b1d2c3e4-5f60-4a71-8b92-0c3d4e5f6a70",
  "location_id": "9e8d7c6b-5a49-4382-9271-6f5e4d3c2b10",
  "lead_id": null,
  "contact_id": "5f3a2b1c-0d9e-48f7-a6b5-c4d3e2f10987"
}

appointment.cancelled

An appointment was called off. The slot is free again, the mirrored calendar event is gone and the pending reminder is dropped; the row survives as cancelled rather than being deleted.

Version 1 · Resource appointment · data requires appointments:read

Fires when. POST /appointments/:id/cancel, the agent cancel tool, the clinic agenda moving a cita to a cancelled status, a car leaving stock with visits booked on it, or an external calendar / vendor cancellation picked up by sync.

FieldType
idstring (uuid)
display_idstring (A-…, the ID visible)
statusenum (cancelled)
previous_statusenum: pending_hold|confirmed|completed|no_show | null
kindenum: test_drive|external|block|clinic
starts_atstring (ISO 8601)
ends_atstring (ISO 8601)
appointment_type_idstring (uuid) | null
vehicle_idstring (uuid) | null
owner_user_idstring (uuid) | null
location_idstring (uuid) | null
lead_idstring (uuid) | null
contact_idstring (uuid) | null
{
  "id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "display_id": "A-42",
  "status": "cancelled",
  "previous_status": "confirmed",
  "kind": "external",
  "starts_at": "2026-05-29T14:00:00.000Z",
  "ends_at": "2026-05-29T14:45:00.000Z",
  "appointment_type_id": "3b7c9d10-4e52-4a63-9f81-0c5d2e6a7b48",
  "vehicle_id": null,
  "owner_user_id": "b1d2c3e4-5f60-4a71-8b92-0c3d4e5f6a70",
  "location_id": "9e8d7c6b-5a49-4382-9271-6f5e4d3c2b10",
  "lead_id": null,
  "contact_id": "5f3a2b1c-0d9e-48f7-a6b5-c4d3e2f10987"
}

appointment.completed

The appointment happened: somebody marked it attended. This is the signal an aftercare, a post-visit survey or a billing step hangs off — and the one that says the slot was USED rather than freed.

Version 1 · Resource appointment · data requires appointments:read

Fires when. PATCH /appointments/:id with status=completed, the clinic agenda moving a cita to an attended status, or a vendor poller mirroring the same outcome. Idempotent on the appointment's status epoch: re-reporting the same completion writes no second event, while a real flip-flop (marked attended, put back, attended later) writes two.

FieldType
idstring (uuid)
display_idstring (A-…, the ID visible)
statusenum (completed)
previous_statusenum: pending_hold|confirmed|cancelled|no_show | null
kindenum: test_drive|external|block|clinic
starts_atstring (ISO 8601)
ends_atstring (ISO 8601)
appointment_type_idstring (uuid) | null
vehicle_idstring (uuid) | null
owner_user_idstring (uuid) | null
location_idstring (uuid) | null
lead_idstring (uuid) | null
contact_idstring (uuid) | null
{
  "id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "display_id": "A-42",
  "status": "completed",
  "previous_status": "confirmed",
  "kind": "external",
  "starts_at": "2026-05-29T14:00:00.000Z",
  "ends_at": "2026-05-29T14:45:00.000Z",
  "appointment_type_id": "3b7c9d10-4e52-4a63-9f81-0c5d2e6a7b48",
  "vehicle_id": null,
  "owner_user_id": "b1d2c3e4-5f60-4a71-8b92-0c3d4e5f6a70",
  "location_id": "9e8d7c6b-5a49-4382-9271-6f5e4d3c2b10",
  "lead_id": null,
  "contact_id": "5f3a2b1c-0d9e-48f7-a6b5-c4d3e2f10987"
}

appointment.no_show

Nobody turned up. A DIFFERENT fact from a cancellation: the slot was held and burned rather than released, which is why the two are separate events and not one status change to branch on.

Version 1 · Resource appointment · data requires appointments:read

Fires when. PATCH /appointments/:id with status=no_show, the clinic agenda moving a cita to a no-show status, or a vendor poller mirroring the same outcome. Same status-epoch idempotency as appointment.completed.

FieldType
idstring (uuid)
display_idstring (A-…, the ID visible)
statusenum (no_show)
previous_statusenum: pending_hold|confirmed|cancelled|completed | null
kindenum: test_drive|external|block|clinic
starts_atstring (ISO 8601)
ends_atstring (ISO 8601)
appointment_type_idstring (uuid) | null
vehicle_idstring (uuid) | null
owner_user_idstring (uuid) | null
location_idstring (uuid) | null
lead_idstring (uuid) | null
contact_idstring (uuid) | null
{
  "id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "display_id": "A-42",
  "status": "no_show",
  "previous_status": "confirmed",
  "kind": "external",
  "starts_at": "2026-05-29T14:00:00.000Z",
  "ends_at": "2026-05-29T14:45:00.000Z",
  "appointment_type_id": "3b7c9d10-4e52-4a63-9f81-0c5d2e6a7b48",
  "vehicle_id": null,
  "owner_user_id": "b1d2c3e4-5f60-4a71-8b92-0c3d4e5f6a70",
  "location_id": "9e8d7c6b-5a49-4382-9271-6f5e4d3c2b10",
  "lead_id": null,
  "contact_id": "5f3a2b1c-0d9e-48f7-a6b5-c4d3e2f10987"
}

appointment.reminded

The reminder for an upcoming appointment went out to the customer, on the conversation it was booked from.

Version 1 · Resource appointment · data requires appointments:read

Fires when. The delayed reminder job fires, reminder_lead_minutes before the appointment (GET /appointments/config).

FieldType
idstring (uuid)
display_idstring (A-…, the ID visible)
starts_atstring (ISO 8601)
{
  "id": "7c1f0e2a-6b3d-4a9e-8f12-2b9a1c0d4e5f",
  "display_id": "A-42",
  "starts_at": "2026-05-28T17:00:00.000Z"
}

appointment.imported

An event a person created directly on the shared external calendar was imported as an appointment, and now consumes the same capacity as one booked in Vitrina.

Version 1 · Resource appointment · data requires appointments:read

Fires when. Incremental Google Calendar sync inserts a new untagged event.

FieldType
idstring (uuid)
display_idstring (A-…, the ID visible)
starts_atstring (ISO 8601)
ends_atstring (ISO 8601)
sourceenum (google)
{
  "id": "9a2b8c7d-6e5f-4a30-b1c2-d3e4f5a60718",
  "display_id": "A-43",
  "starts_at": "2026-05-30T13:00:00.000Z",
  "ends_at": "2026-05-30T14:00:00.000Z",
  "source": "google"
}

contact.created

A contact appeared in the workspace, by ANY path. The notice names the contact (resource.id, resource.url); read it with a credential holding contacts:read. With «Incluir datos del recurso», data carries the contact as it was created — when the subscription owner holds contacts:read with unrestricted visibility.

Version 1 · Resource contact · data requires contacts:read

Fires when. Any path that inserts a contact, exactly once each: POST /contacts, a public booking, a CSV or pre-mapped import (one event per created row), an inbound conversation from an unknown sender on any channel, a manual conversation, a marketplace or portal lead, a Mercado Libre order, a website form, and a voice call from an unknown number. A merge creates nothing and fires contact.merged instead; an import row that MATCHED an existing contact fires contact.updated.

FieldType
idstring (uuid) — pass to GET /api/v1/contacts/{id}
external_idstring | null — the identity the creating path keyed on (your own id on an import, meli:{buyerId}, a WhatsApp wa_id, manual:{uuid})
namestring | null
emailstring | null
phonestring | null — E.164 when it could be normalised
lifecycle_stagestring
origin_channelstring | null — the «Canal de origen» a human picked
company_idstring (uuid) | null
created_atstring (ISO 8601)
{
  "id": "5f3a9c1e-2b7d-4e8a-9c0f-1d2e3f4a5b6c",
  "external_id": null,
  "name": "María González",
  "email": "[email protected]",
  "phone": "+56912345678",
  "lifecycle_stage": "unknown",
  "origin_channel": "manual",
  "company_id": null,
  "created_at": "2026-09-21T14:03:11.000Z"
}

contact.updated

A contact's own columns were written. data.updated_fields names WHICH — field names, never values — so a receiver can ignore the writes it does not care about without reading the contact back. The legal identity (RUT, razón social, giro) and the structured address are never in data; read them with a credential holding contacts:read.

Version 1 · Resource contact · data requires contacts:read

Fires when. PATCH /contacts/{id}, an import row that matched an existing contact, block/archive/report-spam, the AI-off switch, and the automatic enrichment that fills a name, an avatar or a channel handle from an inbound message. Consent writes (POST /contacts/bulk-consent, the outreach-consent ledger) and a merge do not fire it.

FieldType
idstring (uuid) — pass to GET /api/v1/contacts/{id}
external_idstring | null
namestring | null
emailstring | null
phonestring | null — E.164 when it could be normalised
lifecycle_stagestring
origin_channelstring | null
company_idstring (uuid) | null
updated_fieldsarray of string — the column names this write touched, in no particular order
updated_atstring (ISO 8601)
from_lifecycle_stagestring | undefined — present only when the stage changed
to_lifecycle_stagestring | undefined — present only when the stage changed
from_company_idstring (uuid) | null | undefined — present only when the company changed
to_company_idstring (uuid) | null | undefined — present only when the company changed
{
  "id": "5f3a9c1e-2b7d-4e8a-9c0f-1d2e3f4a5b6c",
  "external_id": null,
  "name": "María González",
  "email": "[email protected]",
  "phone": "+56912345678",
  "lifecycle_stage": "customer",
  "origin_channel": "manual",
  "company_id": null,
  "updated_fields": [
    "lifecycle_stage"
  ],
  "updated_at": "2026-09-22T11:20:45.000Z",
  "from_lifecycle_stage": "prospect",
  "to_lifecycle_stage": "customer"
}

contact.merged

Two or more contacts were collapsed into one. resource.id is the SURVIVOR and changes.contact_id.from lists the ids that became tombstones — a tombstone still answers GET /contacts/{id}, carrying merged_into_contact_id, so a stored id can be repointed rather than lost.

Version 1 · Resource contact · data requires contacts:read

Fires when. POST /contacts/{id}/merge, the MCP contacts_merge tool, and the automatic collapse the webchat ingress performs when a visitor’s email AND phone both resolve to one existing contact.

FieldType
idstring (uuid) — the survivor
merged_contact_idsarray of string (uuid) — the contacts that became tombstones
merged_countinteger
conversations_reassignedinteger — threads repointed from the tombstones onto the survivor
filled_fieldsarray of string — the survivor’s columns that GAINED a value from a tombstone (names, not values)
namestring | null — the survivor, after the backfill
emailstring | null
phonestring | null
merged_atstring (ISO 8601)
{
  "id": "5f3a9c1e-2b7d-4e8a-9c0f-1d2e3f4a5b6c",
  "merged_contact_ids": [
    "8c41b0d2-6e5f-4a19-b73c-0d5e9f2a1b84"
  ],
  "merged_count": 1,
  "conversations_reassigned": 3,
  "filled_fields": [
    "email",
    "tax_id",
    "tax_id_kind"
  ],
  "name": "María González",
  "email": "[email protected]",
  "phone": "+56912345678",
  "merged_at": "2026-09-22T11:48:02.000Z"
}

team.created

A team was created.

Version 1 · Resource team · data requires teams:read

Fires when. POST /teams.

FieldType
idstring (uuid)
namestring
brandstring | null
hours_modeenum: workspace | override
created_atstring (ISO 8601)
{
  "id": "cccccccc-0000-4000-8000-000000000001",
  "name": "Ventas Providencia",
  "brand": "Providencia",
  "hours_mode": "override",
  "created_at": "2026-09-22T13:00:00.000Z"
}

team.updated

A team’s name, brand or coverage hours changed.

Version 1 · Resource team · data requires teams:read

Fires when. PUT /teams/{id}.

FieldType
idstring (uuid)
namestring
from_hours_modeenum: workspace | override
to_hours_modeenum: workspace | override
created_atstring (ISO 8601)
{
  "id": "cccccccc-0000-4000-8000-000000000001",
  "name": "Ventas Providencia",
  "from_hours_mode": "workspace",
  "to_hours_mode": "override",
  "created_at": "2026-09-22T13:00:00.000Z"
}

team.deleted

A team was deleted. Every messaging_account.default_team_id, lead.team_id and appointment sales_team_id pointing here was set to NULL in the same write — the fan-out DELETE /teams/{id} documents.

Version 1 · Resource team · data requires teams:read

Fires when. DELETE /teams/{id}.

FieldType
idstring (uuid)
namestring
{
  "id": "cccccccc-0000-4000-8000-000000000001",
  "name": "Ventas Providencia"
}

custom_role.created

A custom role was created.

Version 1 · Resource custom_role · data requires roles:read

Fires when. POST /roles.

FieldType
idstring (uuid)
namestring
scopesstring[]
record_visibilityenum: all | assigned_unassigned | assigned
stock_visibilityenum: all | own_locations
{
  "id": "d2d2d2d2-0000-4000-8000-000000000001",
  "name": "Vendedor sucursal",
  "scopes": [
    "conversations:read",
    "conversations:write",
    "leads:read",
    "leads:write"
  ],
  "record_visibility": "assigned",
  "stock_visibility": "own_locations"
}

custom_role.updated

A custom role’s scopes or either visibility ceiling changed — a PERMISSION CHANGE for every member already on it.

Version 1 · Resource custom_role · data requires roles:read

Fires when. PUT /roles/{id}.

FieldType
idstring (uuid)
namestring
scopesstring[]
record_visibilityenum: all | assigned_unassigned | assigned
stock_visibilityenum: all | own_locations
{
  "id": "d2d2d2d2-0000-4000-8000-000000000001",
  "name": "Vendedor sucursal",
  "scopes": [
    "conversations:read",
    "conversations:write",
    "leads:read",
    "leads:write"
  ],
  "record_visibility": "assigned_unassigned",
  "stock_visibility": "own_locations"
}

custom_role.deleted

A custom role was deleted (only possible once it holds no members, or with reassign_to moving them onto a replacement in the same call).

Version 1 · Resource custom_role · data requires roles:read

Fires when. DELETE /roles/{id}.

FieldType
idstring (uuid)
namestring
reassigned_tostring (uuid) | null
{
  "id": "d2d2d2d2-0000-4000-8000-000000000001",
  "name": "Vendedor sucursal",
  "reassigned_to": null
}

macro.created

A macro (saved reply + actions) was created.

Version 1 · Resource macro · data requires macros:read

Fires when. POST /macros.

FieldType
idstring (uuid)
namestring
activeboolean
{
  "id": "d3d3d3d3-0000-4000-8000-000000000001",
  "name": "Cierre agradecido",
  "active": true
}

clinic_professional.created

A professional was added to the clinic roster.

Version 1 · Resource clinic_professional · data requires clinic:read

Fires when. POST /clinic/professionals succeeds (native clinics only).

FieldType
professional_idstring (uuid)
nombrestring
apellidosstring | null
especialidadstring | null
activeboolean
sourceenum: native | healthatom | reservo
{
  "professional_id": "18342d1b-0000-4000-8000-000000000001",
  "nombre": "Ana",
  "apellidos": "Rojas Vidal",
  "especialidad": "Ortodoncia",
  "active": true,
  "source": "native"
}

clinic_professional.updated

A professional's profile changed — their name, speciality, online-booking flag or active state.

Version 1 · Resource clinic_professional · data requires clinic:read

Fires when. PATCH /clinic/professionals/{id} succeeds.

FieldType
professional_idstring (uuid)
nombrestring
apellidosstring | null
especialidadstring | null
activeboolean
active_beforeboolean
sourceenum: native | healthatom | reservo
{
  "professional_id": "18342d1b-0000-4000-8000-000000000001",
  "nombre": "Ana",
  "apellidos": "Rojas Vidal",
  "especialidad": "Ortodoncia",
  "active": true,
  "active_before": true,
  "source": "native"
}

clinic_professional.deleted

A native professional was removed from the roster. Their citas keep the id as provenance.

Version 1 · Resource clinic_professional · data requires clinic:read

Fires when. DELETE /clinic/professionals/{id} succeeds.

FieldType
professional_idstring (uuid)
nombrestring
apellidosstring | null
{
  "professional_id": "18342d1b-0000-4000-8000-000000000001",
  "nombre": "Ana",
  "apellidos": "Rojas Vidal"
}

clinic_service.created

A prestación was added to the catálogo.

Version 1 · Resource clinic_service · data requires clinic:read

Fires when. POST /clinic/services succeeds (native clinics only).

FieldType
service_idstring (uuid)
nombrestring
codigostring | null
categoriastring | null
precio_clpinteger | null
duration_minutesinteger | null
activeboolean
{
  "service_id": "4cf5bc59-0000-4000-8000-000000000001",
  "nombre": "Control de ortodoncia",
  "codigo": "ORT-CTRL",
  "categoria": "Ortodoncia",
  "precio_clp": 28000,
  "duration_minutes": 30,
  "active": true
}

macro.updated

A macro’s content, actions or active flag changed. Takes effect immediately for anyone about to run it.

Version 1 · Resource macro · data requires macros:read

Fires when. PUT /macros/{id}.

FieldType
idstring (uuid)
namestring
activeboolean
{
  "id": "d3d3d3d3-0000-4000-8000-000000000001",
  "name": "Cierre agradecido",
  "active": false
}

macro.deleted

A macro was deleted. Everything it already did — messages sent, labels applied — stays; only the saved reply itself is gone.

Version 1 · Resource macro · data requires macros:read

Fires when. DELETE /macros/{id}.

FieldType
idstring (uuid)
namestring
{
  "id": "d3d3d3d3-0000-4000-8000-000000000001",
  "name": "Cierre agradecido"
}

assignment_rule.created

A routing rule was created. New rules always append last.

Version 1 · Resource assignment_rule · data requires routing:read

Fires when. POST /assignment-rules.

FieldType
idstring (uuid)
namestring
prioritynumber (int) — evaluation order, ascending
team_idstring (uuid) | null
{
  "id": "d5d5d5d5-0000-4000-8000-000000000001",
  "name": "Mercado Libre → equipo Providencia",
  "priority": 0,
  "team_id": "cccccccc-0000-4000-8000-000000000001"
}

assignment_rule.updated

A rule’s match filters, destination or enabled flag changed. (Its priority changes only via PUT /assignment-rules/order, which rewrites every rule at once and does not fire this per-rule event.)

Version 1 · Resource assignment_rule · data requires routing:read

Fires when. PATCH /assignment-rules/{id}.

FieldType
idstring (uuid)
namestring
enabledboolean
{
  "id": "d5d5d5d5-0000-4000-8000-000000000001",
  "name": "Mercado Libre → equipo Providencia",
  "enabled": false
}

assignment_rule.deleted

A routing rule was deleted. Every other rule keeps its own priority.

Version 1 · Resource assignment_rule · data requires routing:read

Fires when. DELETE /assignment-rules/{id}.

FieldType
idstring (uuid)
namestring
{
  "id": "d5d5d5d5-0000-4000-8000-000000000001",
  "name": "Mercado Libre → equipo Providencia"
}

sla_policy.created

An SLA policy was created.

Version 1 · Resource sla_policy · data requires slas:read

Fires when. POST /slas.

FieldType
idstring (uuid)
namestring
target_first_response_minutesnumber (int) | null
target_resolution_minutesnumber (int) | null
{
  "id": "d7d7d7d7-0000-4000-8000-000000000001",
  "name": "Soporte estándar",
  "target_first_response_minutes": 30,
  "target_resolution_minutes": 1440
}

sla_policy.updated

An SLA policy’s targets, applies_to filter or business_hours_only changed.

Version 1 · Resource sla_policy · data requires slas:read

Fires when. PUT /slas/{id}.

FieldType
idstring (uuid)
namestring
target_first_response_minutesnumber (int) | null
target_resolution_minutesnumber (int) | null
{
  "id": "d7d7d7d7-0000-4000-8000-000000000001",
  "name": "Soporte estándar",
  "target_first_response_minutes": 20,
  "target_resolution_minutes": 1440
}

sla_policy.deleted

An SLA policy was deleted. Tickets it already applied to keep whatever coverage state they already reached; nothing is recomputed.

Version 1 · Resource sla_policy · data requires slas:read

Fires when. DELETE /slas/{id}.

FieldType
idstring (uuid)
namestring
{
  "id": "d7d7d7d7-0000-4000-8000-000000000001",
  "name": "Soporte estándar"
}

automation_trigger.created

An automation trigger was created.

Version 1 · Resource automation_trigger · data requires triggers:read

Fires when. POST /triggers.

FieldType
idstring (uuid)
namestring
eventstring
enabledboolean
{
  "id": "d8d8d8d8-0000-4000-8000-000000000001",
  "name": "Avisar si nadie responde en 2 horas",
  "event": "conversation_idle",
  "enabled": true
}

automation_trigger.updated

A trigger’s event, conditions, actions or enabled flag changed.

Version 1 · Resource automation_trigger · data requires triggers:read

Fires when. PUT /triggers/{id}.

FieldType
idstring (uuid)
namestring
eventstring
enabledboolean
{
  "id": "d8d8d8d8-0000-4000-8000-000000000001",
  "name": "Avisar si nadie responde en 2 horas",
  "event": "conversation_idle",
  "enabled": false
}

automation_trigger.deleted

A trigger was deleted. It stops firing again; past runs are not undone.

Version 1 · Resource automation_trigger · data requires triggers:read

Fires when. DELETE /triggers/{id}.

FieldType
idstring (uuid)
namestring
{
  "id": "d8d8d8d8-0000-4000-8000-000000000001",
  "name": "Avisar si nadie responde en 2 horas"
}

sending_domain.created

A workspace requested its own sending domain. dkim_status/mail_from_status start pending — see sending_domain.verified for the transition once DNS propagates.

Version 1 · Resource sending_domain · data requires messaging_accounts:read

Fires when. POST /sending-domains.

FieldType
idstring (uuid)
domainstring
{
  "id": "d9d9d9d9-0000-4000-8000-000000000001",
  "domain": "mail.autosdelvalle.cl"
}

sending_domain.deleted

A sending domain was removed.

Version 1 · Resource sending_domain · data requires messaging_accounts:read

Fires when. DELETE /sending-domains/{id}.

FieldType
idstring (uuid)
domainstring
{
  "id": "d9d9d9d9-0000-4000-8000-000000000001",
  "domain": "mail.autosdelvalle.cl"
}

whatsapp_template.created

A WhatsApp template was submitted to Meta. status starts PENDING — see whatsapp_template.status_changed for the approval verdict.

Version 1 · Resource whatsapp_template · data requires campaigns:read

Fires when. POST /whatsapp-templates.

FieldType
idstring (uuid)
namestring
languagestring
statusenum: PENDING | APPROVED | REJECTED | …
{
  "id": "e2e2e2e2-0000-4000-8000-000000000001",
  "name": "seguimiento_stock",
  "language": "es_CL",
  "status": "PENDING"
}

whatsapp_template.status_changed

Meta’s async review landed — read off the periodic reconcile poll or a manual POST /whatsapp-templates/sync. rejection_reason is set only when to_status is REJECTED.

Version 1 · Resource whatsapp_template · data requires campaigns:read

Fires when. The template-sync worker, or POST /whatsapp-templates/sync, sees a status change.

FieldType
idstring (uuid)
namestring
from_statusenum: PENDING | APPROVED | REJECTED | …
to_statusenum: PENDING | APPROVED | REJECTED | …
rejection_reasonstring | null
{
  "id": "e2e2e2e2-0000-4000-8000-000000000001",
  "name": "seguimiento_stock",
  "from_status": "PENDING",
  "to_status": "APPROVED",
  "rejection_reason": null
}

whatsapp_template.deleted

A WhatsApp template’s local mirror was removed. Does not withdraw it from Meta.

Version 1 · Resource whatsapp_template · data requires campaigns:read

Fires when. DELETE /whatsapp-templates/{id}.

FieldType
idstring (uuid)
namestring
{
  "id": "e2e2e2e2-0000-4000-8000-000000000001",
  "name": "seguimiento_stock"
}

whatsapp_flow.created

A WhatsApp Flow was created in the builder. Always starts DRAFT.

Version 1 · Resource whatsapp_flow · data requires campaigns:read

Fires when. POST /whatsapp-flows/managed.

FieldType
idstring (uuid)
namestring
statusenum: DRAFT | PUBLISHED | DEPRECATED | BLOCKED | THROTTLED
{
  "id": "e3e3e3e3-0000-4000-8000-000000000001",
  "name": "Agenda tu visita",
  "status": "DRAFT"
}

whatsapp_flow.published

A Flow was published. Publishing is one-way — POST /whatsapp-flows/managed/{id}/duplicate is the only way to keep editing after this.

Version 1 · Resource whatsapp_flow · data requires campaigns:read

Fires when. POST /whatsapp-flows/managed/{id}/publish.

FieldType
idstring (uuid)
namestring
statusenum: DRAFT | PUBLISHED | DEPRECATED | BLOCKED | THROTTLED
{
  "id": "e3e3e3e3-0000-4000-8000-000000000001",
  "name": "Agenda tu visita",
  "status": "PUBLISHED"
}

whatsapp_flow.deprecated

A published Flow was deprecated — stops being offered as new; conversations already inside it are unaffected.

Version 1 · Resource whatsapp_flow · data requires campaigns:read

Fires when. POST /whatsapp-flows/managed/{id}/deprecate.

FieldType
idstring (uuid)
namestring
statusenum: DRAFT | PUBLISHED | DEPRECATED | BLOCKED | THROTTLED
{
  "id": "e3e3e3e3-0000-4000-8000-000000000001",
  "name": "Agenda tu visita",
  "status": "DEPRECATED"
}

whatsapp_flow.deleted

A draft Flow was deleted. A published Flow cannot be (409).

Version 1 · Resource whatsapp_flow · data requires campaigns:read

Fires when. DELETE /whatsapp-flows/managed/{id}.

FieldType
idstring (uuid)
namestring
{
  "id": "e3e3e3e3-0000-4000-8000-000000000001",
  "name": "Agenda tu visita"
}

channel.connected

A messaging channel finished connecting.

Version 1 · Resource channel · data requires messaging_accounts:read

Fires when. POST /messaging-accounts, or a provider OAuth/Atribu callback completing.

FieldType
idstring (uuid)
namestring
kindstring
channelsstring[]
{
  "id": "e4e4e4e4-0000-4000-8000-000000000001",
  "name": "WhatsApp Providencia",
  "kind": "whatsapp_cloud",
  "channels": [
    "whatsapp"
  ]
}

channel.updated

A channel’s config changed (routing default, assignment mode, name).

Version 1 · Resource channel · data requires messaging_accounts:read

Fires when. PUT /messaging-accounts/{id}.

FieldType
idstring (uuid)
namestring
kindstring
{
  "id": "e4e4e4e4-0000-4000-8000-000000000001",
  "name": "WhatsApp Providencia",
  "kind": "whatsapp_cloud"
}

channel.disconnected

A channel was disconnected. Every conversation, message and lead it already produced is untouched — this ends the connection, not the history.

Version 1 · Resource channel · data requires messaging_accounts:read

Fires when. DELETE /messaging-accounts/{id}.

FieldType
idstring (uuid)
namestring
kindstring
{
  "id": "e4e4e4e4-0000-4000-8000-000000000001",
  "name": "WhatsApp Providencia",
  "kind": "whatsapp_cloud"
}

membership.created

A person joined the workspace — added directly (POST /memberships) or by accepting an invitation.

Version 1 · Resource membership · data requires memberships:read

Fires when. POST /memberships, or POST /memberships/invitations/by-token/{token}/accept.

FieldType
idstring (uuid)
user_idstring (uuid)
roleenum: owner | admin | supervisor | agent | consultant
custom_role_idstring (uuid) | null
{
  "id": "11111111-0000-4000-8000-000000000001",
  "user_id": "11111111-0000-4000-8000-000000000001",
  "role": "agent",
  "custom_role_id": "d2d2d2d2-0000-4000-8000-000000000001"
}

membership.updated

A member’s role, custom role, account status or sucursal posting changed.

Version 1 · Resource membership · data requires memberships:read

Fires when. PUT /memberships/{id}.

FieldType
idstring (uuid)
user_idstring (uuid)
from_roleenum: owner | admin | supervisor | agent | consultant
to_roleenum: owner | admin | supervisor | agent | consultant
{
  "id": "11111111-0000-4000-8000-000000000001",
  "user_id": "11111111-0000-4000-8000-000000000001",
  "from_role": "agent",
  "to_role": "supervisor"
}

membership.removed

A person was removed from the workspace. Their open conversations and leads were handed over in the SAME write (handover: unassign | user | round_robin) — a removal never leaves them assigned to someone who can no longer see them.

Version 1 · Resource membership · data requires memberships:read

Fires when. DELETE /memberships/{id}.

FieldType
idstring (uuid)
user_idstring (uuid)
handover_modeenum: unassign | user | round_robin
conversations_movednumber (int)
leads_movednumber (int)
{
  "id": "11111111-0000-4000-8000-000000000002",
  "user_id": "11111111-0000-4000-8000-000000000003",
  "handover_mode": "round_robin",
  "conversations_moved": 6,
  "leads_moved": 3
}

clinic_service.updated

A prestación changed — its price, its duration or whether it is bookable. The price here is the catálogo's own; a patient's price also depends on the arancel and the rules.

Version 1 · Resource clinic_service · data requires clinic:read

Fires when. PATCH /clinic/services/{id} succeeds.

FieldType
service_idstring (uuid)
nombrestring
precio_clpinteger | null
precio_clp_beforeinteger | null
duration_minutesinteger | null
activeboolean
active_beforeboolean
{
  "service_id": "4cf5bc59-0000-4000-8000-000000000001",
  "nombre": "Control de ortodoncia",
  "precio_clp": 28000,
  "precio_clp_before": 25000,
  "duration_minutes": 30,
  "active": true,
  "active_before": true
}

clinic_service.deleted

A native prestación was deleted from the catálogo.

Version 1 · Resource clinic_service · data requires clinic:read

Fires when. DELETE /clinic/services/{id} succeeds.

FieldType
service_idstring (uuid)
nombrestring
{
  "service_id": "4cf5bc59-0000-4000-8000-000000000001",
  "nombre": "Control de ortodoncia"
}

clinic_price_list.updated

An arancel changed — its own fields, or the prices inside it. upserted and removed count the entries the edit touched; a removed entry means "this list does not price it", which is not the same as pricing it at zero.

Version 1 · Resource clinic_price_list · data requires clinic:read

Fires when. PATCH /clinic/pricing/lists/{id} or PUT /clinic/pricing/lists/{id}/entries succeeds.

FieldType
price_list_idstring (uuid)
namestring
upsertedinteger
removedinteger
is_defaultboolean
activeboolean
{
  "price_list_id": "38b62be4-0000-4000-8000-000000000001",
  "name": "Arancel particular 2026",
  "upserted": 2,
  "removed": 0,
  "is_default": true,
  "active": true
}

clinic_pack_purchase.created

A patient bought a pack of sessions. The patient is named by ID only; read the purchase to see who, with a credential that may.

Version 1 · Resource clinic_pack_purchase · data requires clinic:read

Fires when. POST /clinic/packs/purchases succeeds.

FieldType
purchase_idstring (uuid)
pack_idstring (uuid) | null
clinic_patient_idstring (uuid) | null
contact_idstring (uuid) | null
namestring
session_countinteger
precio_clpinteger | null
expires_atYYYY-MM-DD | null
{
  "purchase_id": "9a1f0c77-0000-4000-8000-000000000001",
  "pack_id": "7d2b51a0-0000-4000-8000-000000000001",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "contact_id": "22222222-0000-4000-8000-000000000001",
  "name": "Pack 4 controles de ortodoncia",
  "session_count": 4,
  "precio_clp": 96000,
  "expires_at": "2027-03-21"
}

clinic_pack_purchase.session_consumed

A session of a pack was used — at a cita, or recorded by hand. A reversal fires nothing: it restores the count, and the next consumption reports it.

Version 1 · Resource clinic_pack_purchase · data requires clinic:read

Fires when. POST /clinic/packs/purchases/{id}/consume succeeds, or a cita consumes a session automatically.

FieldType
purchase_idstring (uuid)
session_idstring (uuid)
clinic_patient_idstring (uuid) | null
clinic_service_idstring (uuid) | null
appointment_idstring (uuid) | null
sessions_usedinteger
sessions_used_beforeinteger
session_countinteger
{
  "purchase_id": "9a1f0c77-0000-4000-8000-000000000001",
  "session_id": "2f9c8e51-0000-4000-8000-000000000001",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
  "appointment_id": null,
  "sessions_used": 1,
  "sessions_used_before": 0,
  "session_count": 4
}

clinic_pack_purchase.cancelled

A pack purchase was anulada — with or without a refund. The sessions it still held are gone with it.

Version 1 · Resource clinic_pack_purchase · data requires clinic:read

Fires when. POST /clinic/packs/purchases/{id}/cancel succeeds.

FieldType
purchase_idstring (uuid)
clinic_patient_idstring (uuid) | null
reasonstring
refundedboolean
sessions_remaininginteger
{
  "purchase_id": "9a1f0c77-0000-4000-8000-000000000001",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "reason": "El paciente desistió antes de la primera sesión",
  "refunded": false,
  "sessions_remaining": 4
}

clinic_budget.created

A presupuesto was created as a draft — and drew its folio. The folio is a legal sequence with no gaps, so a draft that is thrown away leaves its number spent.

Version 1 · Resource clinic_budget · data requires clinic_money:read

Fires when. POST /clinic/budgets succeeds.

FieldType
budget_idstring (uuid)
foliostring (the ID visible, e.g. E-47) | null
statusenum: draft | sent | accepted | rejected | voided | expired
clinic_patient_idstring (uuid) | null
contact_idstring (uuid) | null
treatment_plan_idstring (uuid) | null
total_clpinteger | null
valid_untilYYYY-MM-DD | null
{
  "budget_id": "4b1f9d62-0000-4000-8000-000000000001",
  "folio": "E-47",
  "status": "draft",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "contact_id": "1a73af9e-0000-4000-8000-000000000001",
  "treatment_plan_id": "8c5a2e11-0000-4000-8000-000000000001",
  "total_clp": 139000,
  "valid_until": "2026-12-31"
}

clinic_budget.sent

A presupuesto was sent to the patient, with a link they can accept from. delivered says how it left — a channel, or none when the clinic had no way to reach them and handed it over in person.

Version 1 · Resource clinic_budget · data requires clinic_money:read

Fires when. POST /clinic/budgets/{id}/send succeeds.

FieldType
budget_idstring (uuid)
foliostring | null
statusenum: sent
status_beforeenum: draft | sent
clinic_patient_idstring (uuid) | null
contact_idstring (uuid) | null
total_clpinteger | null
deliveredenum: whatsapp | email | none
{
  "budget_id": "4b1f9d62-0000-4000-8000-000000000001",
  "folio": "E-47",
  "status": "sent",
  "status_before": "draft",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "contact_id": "1a73af9e-0000-4000-8000-000000000001",
  "total_clp": 139000,
  "delivered": "whatsapp"
}

clinic_budget.accepted

The presupuesto was accepted — by the patient from their link, or by the clinic recording it. Acceptance opens what the patient owes: obligation_group_id is the group the cobros hang from.

Version 1 · Resource clinic_budget · data requires clinic_money:read

Fires when. POST /clinic/budgets/{id}/accept succeeds, or the patient accepts from their own link.

FieldType
budget_idstring (uuid)
foliostring | null
statusenum: accepted
clinic_patient_idstring (uuid) | null
contact_idstring (uuid) | null
total_clpinteger | null
obligation_group_idstring (uuid) | null
treatment_plan_idstring (uuid) | null
{
  "budget_id": "4b1f9d62-0000-4000-8000-000000000001",
  "folio": "E-47",
  "status": "accepted",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "contact_id": "1a73af9e-0000-4000-8000-000000000001",
  "total_clp": 139000,
  "obligation_group_id": "a067dce6-0000-4000-8000-000000000001",
  "treatment_plan_id": "8c5a2e11-0000-4000-8000-000000000001"
}

clinic_budget.rejected

The patient declined the presupuesto. The folio stays spent.

Version 1 · Resource clinic_budget · data requires clinic_money:read

Fires when. POST /clinic/budgets/{id}/reject succeeds, or the patient declines from their link.

FieldType
budget_idstring (uuid)
foliostring | null
statusenum: rejected
clinic_patient_idstring (uuid) | null
total_clpinteger | null
reasonstring | null
{
  "budget_id": "4b1f9d62-0000-4000-8000-000000000001",
  "folio": "E-47",
  "status": "rejected",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "total_clp": 139000,
  "reason": "El paciente prefiere esperar al próximo semestre"
}

clinic_budget.voided

A presupuesto was voided — never deleted. The folio stays, the document stays listable, and the reason is part of it.

Version 1 · Resource clinic_budget · data requires clinic_money:read

Fires when. POST /clinic/budgets/{id}/void succeeds.

FieldType
budget_idstring (uuid)
foliostring | null
statusenum: voided
clinic_patient_idstring (uuid) | null
total_clpinteger | null
reasonstring
{
  "budget_id": "4b1f9d62-0000-4000-8000-000000000001",
  "folio": "E-47",
  "status": "voided",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "total_clp": 139000,
  "reason": "Se emitió con el arancel equivocado"
}

clinic_budget.mirror_changed

A presupuesto composed from a mirrored Dentalink plan no longer matches that plan: a line was added, removed or repriced in Dentalink after the photo was taken — even when the total stayed the same. The presupuesto itself does not change; the clinic issues a new version, which re-photographs the plan. Fires once per presupuesto.

Version 1 · Resource clinic_budget · data requires clinic_money:read

Fires when. The drift sweep after a mirror sync finds the live plan different from the snapshot.

FieldType
budget_idstring (uuid)
foliostring | null
statusenum: draft | sent | accepted | rejected | expired
clinic_patient_idstring (uuid) | null
contact_idstring (uuid) | null
treatment_plan_idstring (uuid) | null
total_clpinteger | null
valid_untilYYYY-MM-DD | null
mirror_plan_idstring (uuid) | null
mirror_snapshot_atstring (ISO 8601) | null
mirror_changed_atstring (ISO 8601) | null
{
  "budget_id": "4b1f9d62-0000-4000-8000-000000000001",
  "folio": "E-47",
  "status": "sent",
  "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "contact_id": "1a73af9e-0000-4000-8000-000000000001",
  "treatment_plan_id": null,
  "total_clp": 139000,
  "valid_until": "2026-12-31",
  "mirror_plan_id": "6d2c1f0a-0000-4000-8000-000000000001",
  "mirror_snapshot_at": "2026-09-20T14:02:11.000Z",
  "mirror_changed_at": "2026-09-23T10:15:40.000Z"
}

clinic_cash_session.opened

Somebody opened the drawer at a branch. A shift always records WHO opened it, which is why an API key cannot open one.

Version 1 · Resource clinic_cash_session · data requires clinic_money:read

Fires when. POST /clinic/cash-sessions succeeds.

FieldType
cash_session_idstring (uuid)
location_idstring (uuid)
statusenum: open | closed | reconciled
opened_by_user_idstring (uuid) | null
opening_float_clpinteger | null
opened_atISO 8601 timestamp | null
{
  "cash_session_id": "5e7c11a4-0000-4000-8000-000000000001",
  "location_id": "b1b1b1b1-0000-4000-8000-000000000002",
  "status": "open",
  "opened_by_user_id": "11111111-0000-4000-8000-000000000001",
  "opening_float_clp": 30000,
  "opened_at": "2026-09-24T12:00:00.000Z"
}

clinic_cash_session.closed

The drawer was closed against a count. difference_clp is what the till held minus what the ledger expected — a number worth an alert when it is not zero.

Version 1 · Resource clinic_cash_session · data requires clinic_money:read

Fires when. POST /clinic/cash-sessions/{id}/close succeeds.

FieldType
cash_session_idstring (uuid)
location_idstring (uuid)
statusenum: closed
status_beforeenum: open
expected_clpinteger | null
counted_clpinteger | null
difference_clpinteger | null
closed_atISO 8601 timestamp | null
{
  "cash_session_id": "5e7c11a4-0000-4000-8000-000000000001",
  "location_id": "b1b1b1b1-0000-4000-8000-000000000002",
  "status": "closed",
  "status_before": "open",
  "expected_clp": 50000,
  "counted_clp": 50000,
  "difference_clp": 0,
  "closed_at": "2026-09-24T21:05:00.000Z"
}

clinic_patient.created

A ficha was opened for a patient. The notice names the id and nothing else — a name is the first thing the register is sensitive about.

Version 1 · Resource clinic_patient · data requires clinic_patients:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/patients succeeds.

FieldType
patient_idstring (uuid)
sourceenum: native | healthatom | reservo
{
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "source": "native"
}

clinic_patient.updated

A patient's identity page changed. fields names WHICH fields moved, never their values.

Version 1 · Resource clinic_patient · data requires clinic_patients:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. PATCH /clinic/patients/{id} succeeds.

FieldType
patient_idstring (uuid)
fieldsstring[] (the field names that changed)
{
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "fields": [
    "prevision",
    "comuna"
  ]
}

clinic_patient.deleted

A native ficha was deleted. Its contact link survives with a null clinic_patient_id.

Version 1 · Resource clinic_patient · data requires clinic_patients:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. DELETE /clinic/patients/{id} succeeds.

FieldType
patient_idstring (uuid)
{
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001"
}

clinic_patient.merged

Two fichas became one. patient_id is the SURVIVOR and merged_patient_id the one that stopped existing — a mirror that kept the loser has to re-point it.

Version 1 · Resource clinic_patient · data requires clinic_patients:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/patients/{id}/merge succeeds.

FieldType
patient_idstring (uuid) — the survivor
merged_patient_idstring (uuid) — the ficha that was merged away
moved_rowsinteger
{
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "merged_patient_id": "42db9ec9-0000-4000-8000-000000000001",
  "moved_rows": 14
}

clinic_encounter.opened

An atención was opened for a patient. Nothing of what is written in it ever rides in the event.

Version 1 · Resource clinic_encounter · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/patients/{id}/encounters succeeds.

FieldType
encounter_idstring (uuid)
patient_idstring (uuid)
professional_idstring (uuid) | null
appointment_idstring (uuid) | null
kindenum: consulta | sesion | control | evaluacion | procedimiento | teleconsulta
{
  "encounter_id": "14141414-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "professional_id": "18342d1b-0000-4000-8000-000000000001",
  "appointment_id": "eeeeeeee-0000-4000-8000-000000000001",
  "kind": "control"
}

clinic_encounter.closed

An atención was closed. From here its signed notes are amended, never edited.

Version 1 · Resource clinic_encounter · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/encounters/{id}/close succeeds.

FieldType
encounter_idstring (uuid)
patient_idstring (uuid)
closed_atISO 8601 timestamp
{
  "encounter_id": "14141414-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "closed_at": "2026-09-24T14:05:11.900Z"
}

clinic_note.signed

A clinical note was signed. Signing is what makes it part of the record — and what stops it being edited.

Version 1 · Resource clinic_note · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/encounters/{id}/notes with sign: true, or POST /clinic/notes/{id}/sign.

FieldType
note_idstring (uuid)
encounter_idstring (uuid)
patient_idstring (uuid)
kindenum: anamnesis | evolucion | indicaciones | interconsulta | administrativa
signed_by_user_idstring (uuid) | null
{
  "note_id": "18e0d9c3-0000-4000-8000-000000000001",
  "encounter_id": "14141414-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "kind": "evolucion",
  "signed_by_user_id": "11111111-0000-4000-8000-000000000001"
}

clinic_note.amended

A signed note was amended. The amendment is a NEW signed note pointing at the one it corrects; both stay.

Version 1 · Resource clinic_note · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/notes/{id}/amend succeeds.

FieldType
note_idstring (uuid) — the amendment
amends_note_idstring (uuid) — the note it corrects
patient_idstring (uuid)
{
  "note_id": "18e0d9c3-0000-4000-8000-000000000002",
  "amends_note_id": "18e0d9c3-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001"
}

A patient signed a consent. What they agreed to — photographs, marketing use — is in the consent, not here.

Version 1 · Resource clinic_consent · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/consents/{id}/sign succeeds, or the patient signs from their own link.

FieldType
consent_idstring (uuid)
patient_idstring (uuid)
template_idstring (uuid)
signature_methodenum: tablet | public_link | whatsapp_otp | pdf_upload | paper_scan
expires_atISO 8601 timestamp | null
{
  "consent_id": "1b1f8a70-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "template_id": "2c9d4e11-0000-4000-8000-000000000001",
  "signature_method": "tablet",
  "expires_at": null
}

A patient withdrew a consent. The signed document stays — what changed is the permission, and a system holding their photographs has to act on it.

Version 1 · Resource clinic_consent · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/consents/{id}/revoke succeeds.

FieldType
consent_idstring (uuid)
patient_idstring (uuid)
revoked_atISO 8601 timestamp
{
  "consent_id": "1b1f8a70-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "revoked_at": "2026-09-24T18:40:02.119Z"
}

clinic_document.uploaded

A file landed in a patient's ficha. The bytes are fetched deliberately, with a credential; the notice says only that they exist.

Version 1 · Resource clinic_document · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/patients/{id}/documents succeeds.

FieldType
document_idstring (uuid)
patient_idstring (uuid)
kindenum: examen | imagen | informe | receta | indicaciones | consentimiento | presupuesto | boleta | certificado
sensitivityenum: phi | administrative
size_bytesinteger
{
  "document_id": "16161616-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "kind": "informe",
  "sensitivity": "phi",
  "size_bytes": 148223
}

clinic_document.deleted

A clinical document was deleted, with its reason. A mirror that copied it should delete its copy.

Version 1 · Resource clinic_document · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. POST /clinic/documents/{id}/delete succeeds, or retention swept it.

FieldType
document_idstring (uuid)
patient_idstring (uuid)
reasonstring
{
  "document_id": "16161616-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "reason": "Cargado en la ficha equivocada"
}

clinic_privacy_request.completed

A Ley 21.719 request finished — an export the patient can download, or an erase that removed their record. An integration holding a copy has to do the same.

Version 1 · Resource clinic_privacy_request · data requires clinic_record:read

Dato sensible (health data): always delivered as the notice, without data, whatever the subscription asked for.

Fires when. The export archive is written, or the erasure completes.

FieldType
privacy_request_idstring (uuid)
patient_idstring (uuid)
kindenum: export | erase
statusenum: completed | refused | failed
completed_atISO 8601 timestamp | null
{
  "privacy_request_id": "17171717-0000-4000-8000-000000000001",
  "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
  "kind": "export",
  "status": "completed",
  "completed_at": "2026-09-24T18:20:07.430Z"
}

On this page

The envelopeNotice or dataThe headersThe signatureVerifying, in pseudocodeRetriesAuto-pauseThe eventscampaign.sentcampaign.pausedsending_domain.verifiedcoach.proposals_readycoach.finding_readyvoice.autonomous_callsafety_hold.placedsafety_hold.ack_overduesafety_hold.first_response_overduechileautos.leadyapo.leadwebsite.leadconsignment.candidate.createdquote.issuedquote.voidedquote.extendedquote.archivedreservation.createdreservation.voidedreservation.abono_disposedreservation.convertedsale_note.issuedsale_note.approvedsale_note.voidedpurchase_note.issuedpurchase_note.correctedpurchase_note.voidedpayment.recordedconsignment.createdconsignment.returnedconsignment.soldcredit_application.recordedcredit_application.submittedcredit_application.decidedcredit_application.withdrawnprice_approval.requestedprice_approval.decidedconversation.createdconversation.assignedconversation.resolvedmessage.receivedmessage.sentticket.openedticket.reopenedticket.resolvedticket.closedsla.breachedagent_scenario_run.completedagent_scenario_suite.completedai_agent.publishai_agent_graph.publishcost.budget_alertruntime.budget_exceededkb.source.createkb.source.embedkb.source.updatekb.source.deletekb_file.uploadedkb_file.deletedskill.createdskill.updatedskill.deletedcustom_tool.createdcustom_tool.updatedcustom_tool.deletedlead.createdlead.stage_changedlead.pipeline_changedlead.assignedlead.wonlead.lostlead.unqualifiedmacro.appliedstock.source_retiredstock.syncedvehicle.publishedvehicle.unpublishedvehicle.publish_failedvehicle.createdvehicle.attachment.createdmercadolibre.ordermercadolibre.vis_leadappointment.bookedappointment.rescheduledappointment.cancelledappointment.completedappointment.no_showappointment.remindedappointment.importedcontact.createdcontact.updatedcontact.mergedteam.createdteam.updatedteam.deletedcustom_role.createdcustom_role.updatedcustom_role.deletedmacro.createdclinic_professional.createdclinic_professional.updatedclinic_professional.deletedclinic_service.createdmacro.updatedmacro.deletedassignment_rule.createdassignment_rule.updatedassignment_rule.deletedsla_policy.createdsla_policy.updatedsla_policy.deletedautomation_trigger.createdautomation_trigger.updatedautomation_trigger.deletedsending_domain.createdsending_domain.deletedwhatsapp_template.createdwhatsapp_template.status_changedwhatsapp_template.deletedwhatsapp_flow.createdwhatsapp_flow.publishedwhatsapp_flow.deprecatedwhatsapp_flow.deletedchannel.connectedchannel.updatedchannel.disconnectedmembership.createdmembership.updatedmembership.removedclinic_service.updatedclinic_service.deletedclinic_price_list.updatedclinic_pack_purchase.createdclinic_pack_purchase.session_consumedclinic_pack_purchase.cancelledclinic_budget.createdclinic_budget.sentclinic_budget.acceptedclinic_budget.rejectedclinic_budget.voidedclinic_budget.mirror_changedclinic_cash_session.openedclinic_cash_session.closedclinic_patient.createdclinic_patient.updatedclinic_patient.deletedclinic_patient.mergedclinic_encounter.openedclinic_encounter.closedclinic_note.signedclinic_note.amendedclinic_consent.signedclinic_consent.revokedclinic_document.uploadedclinic_document.deletedclinic_privacy_request.completed