@vitrina/api 15.16.0 → 15.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -22035,7 +22035,7 @@ interface paths {
22035
22035
  put?: never;
22036
22036
  /**
22037
22037
  * Submit a website buyer inquiry (dealer-website write API)
22038
- * @description Creates a CRM lead with `website` origin, attaching the inquired Vehículo when `vehicle_id` is given. A proyecto form (Módulo Proyectos) sends `project_id` and/or `unit_model_id` (a tipología names its proyecto): the lead lands on the proyecto, is routed to its ejecutivos, and `message` becomes optional. `attribution` carries the visitor's `first_touch` and `last_touch` (utm_*, gclid, fbclid, anonymous_id); both are kept on the lead. Requires the write-only `leads:intake` scope (never `leads:write`). Returns a minimized `{ status }` ack — `received` on create, `duplicate` on dedup — and echoes no internal ids. Rate-limited like every authenticated endpoint; a hidden `hp_website` honeypot field silently drops spam.
22038
+ * @description Creates a CRM lead with `website` origin, attaching the inquired Vehículo when `vehicle_id` is given. A proyecto form (Módulo Proyectos) sends `project_id` and/or `unit_model_id` (a tipología names its proyecto): the lead lands on the proyecto, is routed to its ejecutivos, and `message` becomes optional. `attribution` carries the visitor's `first_touch` and `last_touch` (utm_*, gclid, fbclid, anonymous_id); both are kept on the lead. `value_amount` is the lead's value; `quote_amount` is a figure the site quoted that is not the deal (a trade-in quote): it only fills the first-touch template's amount and the inbox card when `value_amount` is absent, and is never stored as the lead's value. Requires the write-only `leads:intake` scope (never `leads:write`). Returns a minimized `{ status }` ack — `received` on create, `duplicate` on dedup — and echoes no internal ids. Rate-limited like every authenticated endpoint; a hidden `hp_website` honeypot field silently drops spam.
22039
22039
  */
22040
22040
  post: {
22041
22041
  parameters: {
@@ -22101,6 +22101,7 @@ interface paths {
22101
22101
  stage_id?: string;
22102
22102
  value_amount?: number;
22103
22103
  value_currency?: string;
22104
+ quote_amount?: number;
22104
22105
  item_label?: string;
22105
22106
  source_ref?: string;
22106
22107
  fields?: {
@@ -88530,6 +88531,272 @@ interface paths {
88530
88531
  patch?: never;
88531
88532
  trace?: never;
88532
88533
  };
88534
+ "/finanzas/contador-export": {
88535
+ parameters: {
88536
+ query?: never;
88537
+ header?: never;
88538
+ path?: never;
88539
+ cookie?: never;
88540
+ };
88541
+ /**
88542
+ * Download the Contador package for a month (zip)
88543
+ * @description One zip, `paquete-contador-<period>.zip`, with seven xlsx: `01-libro-de-compras`, `02-libro-de-ventas`, `03-honorarios`, `04-movimientos-por-cuenta` (one sheet per bank account), `05-clasificaciones-por-cuenta` (net, as the Resultado counts), `06-resultado` and `07-ajustes-posteriores-al-cierre`, each `<name>-<period>.xlsx`, built by the same code as each screen and its export. Read-only. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT; the libro de ventas keeps the patient RUT on facturas and on notas de crédito or débito that cite one, and boletas carry neither RUT nor name. Requires the `finanzas` add-on — without it every Finanzas operation answers `402 ENTITLEMENT_NOT_ACTIVE` (`details.required = ["finanzas"]`). Requires `finanzas:read`, `finanzas_reports:read` and `finanzas:export` (the Contador seat, owners and admins).
88544
+ */
88545
+ get: {
88546
+ parameters: {
88547
+ query?: {
88548
+ period?: string;
88549
+ };
88550
+ header?: never;
88551
+ path?: never;
88552
+ cookie?: never;
88553
+ };
88554
+ requestBody?: never;
88555
+ responses: {
88556
+ /** @description The package, as an attachment */
88557
+ 200: {
88558
+ headers: {
88559
+ [name: string]: unknown;
88560
+ };
88561
+ content: {
88562
+ "application/zip": string;
88563
+ };
88564
+ };
88565
+ /** @description Validation error */
88566
+ 400: {
88567
+ headers: {
88568
+ [name: string]: unknown;
88569
+ };
88570
+ content: {
88571
+ "application/json": components["schemas"]["Error"];
88572
+ };
88573
+ };
88574
+ /** @description Unauthorized */
88575
+ 401: {
88576
+ headers: {
88577
+ [name: string]: unknown;
88578
+ };
88579
+ content: {
88580
+ "application/json": components["schemas"]["Error"];
88581
+ };
88582
+ };
88583
+ /** @description Forbidden */
88584
+ 403: {
88585
+ headers: {
88586
+ [name: string]: unknown;
88587
+ };
88588
+ content: {
88589
+ "application/json": components["schemas"]["Error"];
88590
+ };
88591
+ };
88592
+ /** @description Not found */
88593
+ 404: {
88594
+ headers: {
88595
+ [name: string]: unknown;
88596
+ };
88597
+ content: {
88598
+ "application/json": components["schemas"]["Error"];
88599
+ };
88600
+ };
88601
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
88602
+ 409: {
88603
+ headers: {
88604
+ [name: string]: unknown;
88605
+ };
88606
+ content: {
88607
+ "application/json": components["schemas"]["Error"];
88608
+ };
88609
+ };
88610
+ /** @description Rate limited */
88611
+ 429: {
88612
+ headers: {
88613
+ [name: string]: unknown;
88614
+ };
88615
+ content: {
88616
+ "application/json": components["schemas"]["Error"];
88617
+ };
88618
+ };
88619
+ };
88620
+ };
88621
+ put?: never;
88622
+ post?: never;
88623
+ delete?: never;
88624
+ options?: never;
88625
+ head?: never;
88626
+ patch?: never;
88627
+ trace?: never;
88628
+ };
88629
+ "/finanzas/contador-export/manifest": {
88630
+ parameters: {
88631
+ query?: never;
88632
+ header?: never;
88633
+ path?: never;
88634
+ cookie?: never;
88635
+ };
88636
+ /**
88637
+ * What the Contador package for a month holds
88638
+ * @description The same package as the download, without the bytes: the period, the zip name and each file (key, label, name, rows, size). Over MCP this is the read that generates and checks the package; the zip itself downloads over HTTPS. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT; the libro de ventas keeps the patient RUT on facturas and on notas de crédito or débito that cite one, and boletas carry neither RUT nor name. Requires the `finanzas` add-on — without it every Finanzas operation answers `402 ENTITLEMENT_NOT_ACTIVE` (`details.required = ["finanzas"]`). Requires `finanzas:read`, `finanzas_reports:read` and `finanzas:export` (the Contador seat, owners and admins).
88639
+ */
88640
+ get: {
88641
+ parameters: {
88642
+ query?: {
88643
+ period?: string;
88644
+ };
88645
+ header?: never;
88646
+ path?: never;
88647
+ cookie?: never;
88648
+ };
88649
+ requestBody?: never;
88650
+ responses: {
88651
+ /** @description The manifest */
88652
+ 200: {
88653
+ headers: {
88654
+ [name: string]: unknown;
88655
+ };
88656
+ content: {
88657
+ /**
88658
+ * @example {
88659
+ * "data": {
88660
+ * "period": "2026-09",
88661
+ * "filename": "paquete-contador-2026-09.zip",
88662
+ * "files": [
88663
+ * {
88664
+ * "key": "libro_compras",
88665
+ * "label": "Libro de compras",
88666
+ * "filename": "01-libro-de-compras-2026-09.xlsx",
88667
+ * "rows": 84,
88668
+ * "size_bytes": 14210
88669
+ * },
88670
+ * {
88671
+ * "key": "libro_ventas",
88672
+ * "label": "Libro de ventas",
88673
+ * "filename": "02-libro-de-ventas-2026-09.xlsx",
88674
+ * "rows": 1312,
88675
+ * "size_bytes": 61904
88676
+ * },
88677
+ * {
88678
+ * "key": "honorarios",
88679
+ * "label": "Honorarios",
88680
+ * "filename": "03-honorarios-2026-09.xlsx",
88681
+ * "rows": 12,
88682
+ * "size_bytes": 7480
88683
+ * },
88684
+ * {
88685
+ * "key": "movimientos",
88686
+ * "label": "Movimientos por cuenta",
88687
+ * "filename": "04-movimientos-por-cuenta-2026-09.xlsx",
88688
+ * "rows": 640,
88689
+ * "size_bytes": 38112
88690
+ * },
88691
+ * {
88692
+ * "key": "clasificaciones",
88693
+ * "label": "Clasificaciones por cuenta",
88694
+ * "filename": "05-clasificaciones-por-cuenta-2026-09.xlsx",
88695
+ * "rows": 702,
88696
+ * "size_bytes": 40356
88697
+ * },
88698
+ * {
88699
+ * "key": "resultado",
88700
+ * "label": "Resultado",
88701
+ * "filename": "06-resultado-2026-09.xlsx",
88702
+ * "rows": 1398,
88703
+ * "size_bytes": 52018
88704
+ * },
88705
+ * {
88706
+ * "key": "ajustes",
88707
+ * "label": "Ajustes posteriores al cierre",
88708
+ * "filename": "07-ajustes-posteriores-al-cierre-2026-09.xlsx",
88709
+ * "rows": 3,
88710
+ * "size_bytes": 6920
88711
+ * }
88712
+ * ]
88713
+ * }
88714
+ * }
88715
+ */
88716
+ "application/json": {
88717
+ data: {
88718
+ /** @description The month, `YYYY-MM`. */
88719
+ period: string;
88720
+ /** @description The zip the download serves. */
88721
+ filename: string;
88722
+ files: {
88723
+ /** @enum {string} */
88724
+ key: "libro_compras" | "libro_ventas" | "honorarios" | "movimientos" | "clasificaciones" | "resultado" | "ajustes";
88725
+ label: string;
88726
+ /** @description The xlsx inside the zip. */
88727
+ filename: string;
88728
+ /** @description Rows of its «Detalle» sheet (documents, items) when it has one, else of its first sheet (movements, adjustments). */
88729
+ rows: number;
88730
+ size_bytes: number;
88731
+ }[];
88732
+ };
88733
+ };
88734
+ };
88735
+ };
88736
+ /** @description Validation error */
88737
+ 400: {
88738
+ headers: {
88739
+ [name: string]: unknown;
88740
+ };
88741
+ content: {
88742
+ "application/json": components["schemas"]["Error"];
88743
+ };
88744
+ };
88745
+ /** @description Unauthorized */
88746
+ 401: {
88747
+ headers: {
88748
+ [name: string]: unknown;
88749
+ };
88750
+ content: {
88751
+ "application/json": components["schemas"]["Error"];
88752
+ };
88753
+ };
88754
+ /** @description Forbidden */
88755
+ 403: {
88756
+ headers: {
88757
+ [name: string]: unknown;
88758
+ };
88759
+ content: {
88760
+ "application/json": components["schemas"]["Error"];
88761
+ };
88762
+ };
88763
+ /** @description Not found */
88764
+ 404: {
88765
+ headers: {
88766
+ [name: string]: unknown;
88767
+ };
88768
+ content: {
88769
+ "application/json": components["schemas"]["Error"];
88770
+ };
88771
+ };
88772
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
88773
+ 409: {
88774
+ headers: {
88775
+ [name: string]: unknown;
88776
+ };
88777
+ content: {
88778
+ "application/json": components["schemas"]["Error"];
88779
+ };
88780
+ };
88781
+ /** @description Rate limited */
88782
+ 429: {
88783
+ headers: {
88784
+ [name: string]: unknown;
88785
+ };
88786
+ content: {
88787
+ "application/json": components["schemas"]["Error"];
88788
+ };
88789
+ };
88790
+ };
88791
+ };
88792
+ put?: never;
88793
+ post?: never;
88794
+ delete?: never;
88795
+ options?: never;
88796
+ head?: never;
88797
+ patch?: never;
88798
+ trace?: never;
88799
+ };
88533
88800
  "/clinic/evidence-cards": {
88534
88801
  parameters: {
88535
88802
  query?: never;
@@ -139133,6 +139400,8 @@ interface paths {
139133
139400
  *
139134
139401
  * Each row also carries `campaign_origin` — the newest campaign send stored in the thread (`null` when none): its campaign, when it left, whether the send created the thread, and whether it is still the newest message (`awaiting_reply`). `fromCampaign=true` lists every such thread, closed ones included.
139135
139402
  *
139403
+ * Each row also carries `waiting_since` — when its customer started waiting on a human, `null` when nobody waits (the «Esperando respuesta» rule; see the `waiting` parameter). `waiting=true` lists only waiting conversations, team-wide; `sort=wait` orders urgent first, then the longest wait.
139404
+ *
139136
139405
  * Rows here do NOT carry `ad_origin_nudge` — that field is single-conversation-read only, to avoid a per-row integration lookup on every page of the inbox.
139137
139406
  *
139138
139407
  * `fields=summary` answers the SAME page with only the identifying and funnel scalars per row — `id`, `display_id`, `channel`, `source`, `status`, `handler`, `assignee_user_id`, `contact_id`, `ticket_id`, `awaiting_human_since`, `first_ai_message_at`, `last_message_date`, `created_at`, `ad_origin` — plus `contact_name`, `ticket_status` and `current_stage_id` off the embeds, and `assignee_name` / `unread` when the list computed them (an api_key has no read markers, so no `unread`). No `contact`, `ticket`, `lastMessage`, `tags`, `marketplaceWindow` or `last_call`. `meta` and `counts` are unchanged. A 20-row page is ~147k characters at `full` (the default, byte-identical to omitting the parameter) and a fraction of that at `summary`; page with `summary` to choose, then `GET /conversations/{id}` for one thread whole.
@@ -139164,7 +139433,10 @@ interface paths {
139164
139433
  aiReplied?: boolean | null;
139165
139434
  fromAd?: boolean | null;
139166
139435
  fromCampaign?: boolean | null;
139167
- sort?: "created_at" | "updated_at" | "last_message_date";
139436
+ /** @description true: only conversations where a customer is waiting on a human («Esperando respuesta»), team-wide. Waiting = open or pending, not filtered, on a channel you can reply in, and either an AI handoff nobody on the team has answered yet (a reply from ANY teammate clears it, including WhatsApp Business / Instagram app replies; a silent «Visita acordada» notice needs no answer) or a human-handled thread whose last message is the customer’s; waits older than 7 days are left out. Each row carries `waiting_since`. Pair with `sort=wait` for the queue order. */
139437
+ waiting?: boolean | null;
139438
+ /** @description `wait`: urgent (`handoff_urgent`) first, then by `waiting_since` — `order=desc` (default) is the longest wait first, `asc` the newest; conversations that are not waiting go last. */
139439
+ sort?: "created_at" | "updated_at" | "last_message_date" | "wait";
139168
139440
  order?: "asc" | "desc";
139169
139441
  include?: "counts";
139170
139442
  /** @description `full` (default): the inbox row with its `contact`, `ticket`, `lastMessage`, `tags`, `marketplaceWindow`, `ad_origin` and `last_call`, exactly as without the parameter. `summary`: only the identifying and funnel scalars — `id`, `display_id`, `channel`, `source`, `status`, `handler`, `assignee_user_id`, `contact_id`, `ticket_id`, `awaiting_human_since`, `first_ai_message_at`, `last_message_date`, `created_at`, `ad_origin`, plus `contact_name`, `ticket_status`, `current_stage_id` off the embeds and `assignee_name` / `unread` when the list computed them. Pagination, `meta` and `counts` are unchanged. Use `summary` to page the inbox cheaply, then `GET /conversations/{id}` for one thread in full. */
@@ -139303,7 +139575,8 @@ interface paths {
139303
139575
  * }
139304
139576
  * ],
139305
139577
  * "last_call": null,
139306
- * "handoff_urgent": false
139578
+ * "handoff_urgent": false,
139579
+ * "waiting_since": null
139307
139580
  * },
139308
139581
  * {
139309
139582
  * "id": "bbbbbbbb-0000-4000-8000-000000000002",
@@ -139440,7 +139713,8 @@ interface paths {
139440
139713
  * },
139441
139714
  * "tags": [],
139442
139715
  * "last_call": null,
139443
- * "handoff_urgent": false
139716
+ * "handoff_urgent": false,
139717
+ * "waiting_since": null
139444
139718
  * }
139445
139719
  * ],
139446
139720
  * "meta": {
@@ -139729,9 +140003,11 @@ interface paths {
139729
140003
  cookie?: never;
139730
140004
  };
139731
140005
  /**
139732
- * Inbox badge counts (my open / unread / mentions / unassigned)
140006
+ * Inbox badge counts (my open / unread / mentions / unassigned / waiting)
139733
140007
  * @description The four badge counts on their own, scoped to the calling principal: `myOpen` and `myUnread` are the conversations assigned to the member behind the credential, `myMentions` the ones that @-mention them, `unassigned` the whole workspace’s unowned queue. An API key has no member behind it, so its three "my" counts read 0 and only `unassigned` is meaningful — use a personal token to get a person’s badges.
139734
140008
  *
140009
+ * «Esperando respuesta» (the waiting queue): `waiting` is how many conversations have a customer waiting on a human, team-wide (the list `GET /conversations?waiting=true` returns exactly these); `waitingUrgent` how many of them are flagged urgent; `waitingMine` how many are assigned to the caller (0 for an API key); `waitingOldestSince` when the longest wait started (ISO, `null` when nobody waits) — render it as «el más antiguo 2 h».
140010
+ *
139735
140011
  * `GET /conversations?include=counts` returns the same payload beside a page of conversations; this route exists for the caller that wants the badges without one.
139736
140012
  */
139737
140013
  get: {
@@ -139755,7 +140031,11 @@ interface paths {
139755
140031
  * "myOpen": 3,
139756
140032
  * "myUnread": 1,
139757
140033
  * "unassigned": 2,
139758
- * "myMentions": 0
140034
+ * "myMentions": 0,
140035
+ * "waiting": 3,
140036
+ * "waitingUrgent": 1,
140037
+ * "waitingMine": 1,
140038
+ * "waitingOldestSince": "2026-10-31T13:05:00.000Z"
139759
140039
  * }
139760
140040
  * }
139761
140041
  */
@@ -139994,6 +140274,7 @@ interface paths {
139994
140274
  * ],
139995
140275
  * "last_call": null,
139996
140276
  * "handoff_urgent": false,
140277
+ * "waiting_since": null,
139997
140278
  * "ad_origin_nudge": null,
139998
140279
  * "handoff_resolved_suggested": false,
139999
140280
  * "message": [
@@ -237685,6 +237966,8 @@ interface paths {
237685
237966
  * This car’s lead timeline
237686
237967
  * @description Every named lead that asked about this vehicle, merged with its anonymous Yapo phone-reveals, newest first. This is the car’s HISTORY, so unlike `/interest` a sold vehicle keeps its timeline.
237687
237968
  *
237969
+ * Each lead row carries `conversation_id`: the chat to open for that person (the lead's own conversation, else the contact's most recent one, e.g. the first-touch WhatsApp after a portal inquiry). Null when there is none.
237970
+ *
237688
237971
  * The Stock detail screen merges this with `/vehicles/{id}/history` (the change log) into one "Leads y cambios" feed — they are two endpoints because they are two different kinds of event. Automotive-vertical only: a workspace on another vertical gets 403 here however its scopes are set.
237689
237972
  */
237690
237973
  get: {
@@ -237713,6 +237996,7 @@ interface paths {
237713
237996
  * "kind": "lead",
237714
237997
  * "contact_id": "22222222-0000-4000-8000-000000000001",
237715
237998
  * "lead_id": "99999999-0000-4000-8000-000000000002",
237999
+ * "conversation_id": "99999999-0000-4000-8000-000000000003",
237716
238000
  * "portal": "website",
237717
238001
  * "at": "2026-09-19T15:30:00.000Z"
237718
238002
  * }
@@ -254580,7 +254864,7 @@ interface components {
254580
254864
  perfect_battery_pct?: number;
254581
254865
  currency?: string;
254582
254866
  };
254583
- /** @description Workspace notification rails. The staff WhatsApp rails (`staff_whatsapp`, `unanswered_supervisor_whatsapp`, `booking_whatsapp`) are opt-in: arming one (`enabled: true` with an `account_id`) submits its template to Meta on that number and wires it on approval. Each rail sent replaces that rail whole; rails not sent are kept. */
254867
+ /** @description Workspace notification rails. The staff WhatsApp rails (`staff_whatsapp`, `unanswered_supervisor_whatsapp`, `booking_whatsapp`, `reminder_whatsapp`) are opt-in: arming one (`enabled: true` with an `account_id`) submits its template to Meta on that number and wires it on approval. Each rail sent replaces that rail whole; rails not sent are kept. */
254584
254868
  notifications?: {
254585
254869
  staff_whatsapp?: {
254586
254870
  enabled?: boolean;
@@ -254596,6 +254880,16 @@ interface components {
254596
254880
  template_name?: string;
254597
254881
  template_language?: string;
254598
254882
  user_ids?: string[];
254883
+ /** @description Minutes of open time a conversation waits on the team before the supervisors get the WhatsApp, once per episode. Unset = 60. */
254884
+ delay_minutes?: number;
254885
+ };
254886
+ /** @description Staff WhatsApp on every «sin respuesta» reminder, to the assignee (template aviso_sigue_esperando_v1). Opt-in: each send costs a WhatsApp conversation. */
254887
+ reminder_whatsapp?: {
254888
+ enabled?: boolean;
254889
+ /** Format: uuid */
254890
+ account_id?: string;
254891
+ template_name?: string;
254892
+ template_language?: string;
254599
254893
  };
254600
254894
  booking_whatsapp?: {
254601
254895
  enabled?: boolean;
@@ -254672,6 +254966,26 @@ interface components {
254672
254966
  rejection_reason: string | null;
254673
254967
  }[];
254674
254968
  };
254969
+ reminder_whatsapp: {
254970
+ /** @description The rail is switched on (`enabled: true`). */
254971
+ armed: boolean;
254972
+ /**
254973
+ * @description Worst state across the rail templates (blocked, rejected, missing, pending, approved); `off` when not armed or nothing to provision; `no_number` when armed without a sending number.
254974
+ * @enum {string}
254975
+ */
254976
+ template_state: "off" | "no_number" | "missing" | "pending" | "approved" | "rejected" | "blocked";
254977
+ reason: string | null;
254978
+ /** @description The rail has its template name set (by an admin or on approval). */
254979
+ wired: boolean;
254980
+ templates: {
254981
+ key: string;
254982
+ name: string;
254983
+ language: string;
254984
+ /** @enum {string} */
254985
+ state: "missing" | "pending" | "approved" | "rejected" | "blocked";
254986
+ rejection_reason: string | null;
254987
+ }[];
254988
+ };
254675
254989
  };
254676
254990
  /** @description Workspace display name. */
254677
254991
  name?: string;
@@ -254884,6 +255198,16 @@ interface components {
254884
255198
  template_name?: string;
254885
255199
  template_language?: string;
254886
255200
  user_ids?: string[];
255201
+ /** @description Minutes of open time a conversation waits on the team before the supervisors get the WhatsApp, once per episode. Unset = 60. */
255202
+ delay_minutes?: number;
255203
+ };
255204
+ /** @description Staff WhatsApp on every «sin respuesta» reminder, to the assignee (template aviso_sigue_esperando_v1). Opt-in: each send costs a WhatsApp conversation. */
255205
+ reminder_whatsapp?: {
255206
+ enabled?: boolean;
255207
+ /** Format: uuid */
255208
+ account_id?: string;
255209
+ template_name?: string;
255210
+ template_language?: string;
254887
255211
  };
254888
255212
  booking_whatsapp?: {
254889
255213
  enabled?: boolean;
package/dist/index.d.ts CHANGED
@@ -22035,7 +22035,7 @@ interface paths {
22035
22035
  put?: never;
22036
22036
  /**
22037
22037
  * Submit a website buyer inquiry (dealer-website write API)
22038
- * @description Creates a CRM lead with `website` origin, attaching the inquired Vehículo when `vehicle_id` is given. A proyecto form (Módulo Proyectos) sends `project_id` and/or `unit_model_id` (a tipología names its proyecto): the lead lands on the proyecto, is routed to its ejecutivos, and `message` becomes optional. `attribution` carries the visitor's `first_touch` and `last_touch` (utm_*, gclid, fbclid, anonymous_id); both are kept on the lead. Requires the write-only `leads:intake` scope (never `leads:write`). Returns a minimized `{ status }` ack — `received` on create, `duplicate` on dedup — and echoes no internal ids. Rate-limited like every authenticated endpoint; a hidden `hp_website` honeypot field silently drops spam.
22038
+ * @description Creates a CRM lead with `website` origin, attaching the inquired Vehículo when `vehicle_id` is given. A proyecto form (Módulo Proyectos) sends `project_id` and/or `unit_model_id` (a tipología names its proyecto): the lead lands on the proyecto, is routed to its ejecutivos, and `message` becomes optional. `attribution` carries the visitor's `first_touch` and `last_touch` (utm_*, gclid, fbclid, anonymous_id); both are kept on the lead. `value_amount` is the lead's value; `quote_amount` is a figure the site quoted that is not the deal (a trade-in quote): it only fills the first-touch template's amount and the inbox card when `value_amount` is absent, and is never stored as the lead's value. Requires the write-only `leads:intake` scope (never `leads:write`). Returns a minimized `{ status }` ack — `received` on create, `duplicate` on dedup — and echoes no internal ids. Rate-limited like every authenticated endpoint; a hidden `hp_website` honeypot field silently drops spam.
22039
22039
  */
22040
22040
  post: {
22041
22041
  parameters: {
@@ -22101,6 +22101,7 @@ interface paths {
22101
22101
  stage_id?: string;
22102
22102
  value_amount?: number;
22103
22103
  value_currency?: string;
22104
+ quote_amount?: number;
22104
22105
  item_label?: string;
22105
22106
  source_ref?: string;
22106
22107
  fields?: {
@@ -88530,6 +88531,272 @@ interface paths {
88530
88531
  patch?: never;
88531
88532
  trace?: never;
88532
88533
  };
88534
+ "/finanzas/contador-export": {
88535
+ parameters: {
88536
+ query?: never;
88537
+ header?: never;
88538
+ path?: never;
88539
+ cookie?: never;
88540
+ };
88541
+ /**
88542
+ * Download the Contador package for a month (zip)
88543
+ * @description One zip, `paquete-contador-<period>.zip`, with seven xlsx: `01-libro-de-compras`, `02-libro-de-ventas`, `03-honorarios`, `04-movimientos-por-cuenta` (one sheet per bank account), `05-clasificaciones-por-cuenta` (net, as the Resultado counts), `06-resultado` and `07-ajustes-posteriores-al-cierre`, each `<name>-<period>.xlsx`, built by the same code as each screen and its export. Read-only. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT; the libro de ventas keeps the patient RUT on facturas and on notas de crédito or débito that cite one, and boletas carry neither RUT nor name. Requires the `finanzas` add-on — without it every Finanzas operation answers `402 ENTITLEMENT_NOT_ACTIVE` (`details.required = ["finanzas"]`). Requires `finanzas:read`, `finanzas_reports:read` and `finanzas:export` (the Contador seat, owners and admins).
88544
+ */
88545
+ get: {
88546
+ parameters: {
88547
+ query?: {
88548
+ period?: string;
88549
+ };
88550
+ header?: never;
88551
+ path?: never;
88552
+ cookie?: never;
88553
+ };
88554
+ requestBody?: never;
88555
+ responses: {
88556
+ /** @description The package, as an attachment */
88557
+ 200: {
88558
+ headers: {
88559
+ [name: string]: unknown;
88560
+ };
88561
+ content: {
88562
+ "application/zip": string;
88563
+ };
88564
+ };
88565
+ /** @description Validation error */
88566
+ 400: {
88567
+ headers: {
88568
+ [name: string]: unknown;
88569
+ };
88570
+ content: {
88571
+ "application/json": components["schemas"]["Error"];
88572
+ };
88573
+ };
88574
+ /** @description Unauthorized */
88575
+ 401: {
88576
+ headers: {
88577
+ [name: string]: unknown;
88578
+ };
88579
+ content: {
88580
+ "application/json": components["schemas"]["Error"];
88581
+ };
88582
+ };
88583
+ /** @description Forbidden */
88584
+ 403: {
88585
+ headers: {
88586
+ [name: string]: unknown;
88587
+ };
88588
+ content: {
88589
+ "application/json": components["schemas"]["Error"];
88590
+ };
88591
+ };
88592
+ /** @description Not found */
88593
+ 404: {
88594
+ headers: {
88595
+ [name: string]: unknown;
88596
+ };
88597
+ content: {
88598
+ "application/json": components["schemas"]["Error"];
88599
+ };
88600
+ };
88601
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
88602
+ 409: {
88603
+ headers: {
88604
+ [name: string]: unknown;
88605
+ };
88606
+ content: {
88607
+ "application/json": components["schemas"]["Error"];
88608
+ };
88609
+ };
88610
+ /** @description Rate limited */
88611
+ 429: {
88612
+ headers: {
88613
+ [name: string]: unknown;
88614
+ };
88615
+ content: {
88616
+ "application/json": components["schemas"]["Error"];
88617
+ };
88618
+ };
88619
+ };
88620
+ };
88621
+ put?: never;
88622
+ post?: never;
88623
+ delete?: never;
88624
+ options?: never;
88625
+ head?: never;
88626
+ patch?: never;
88627
+ trace?: never;
88628
+ };
88629
+ "/finanzas/contador-export/manifest": {
88630
+ parameters: {
88631
+ query?: never;
88632
+ header?: never;
88633
+ path?: never;
88634
+ cookie?: never;
88635
+ };
88636
+ /**
88637
+ * What the Contador package for a month holds
88638
+ * @description The same package as the download, without the bytes: the period, the zip name and each file (key, label, name, rows, size). Over MCP this is the read that generates and checks the package; the zip itself downloads over HTTPS. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT; the libro de ventas keeps the patient RUT on facturas and on notas de crédito or débito that cite one, and boletas carry neither RUT nor name. Requires the `finanzas` add-on — without it every Finanzas operation answers `402 ENTITLEMENT_NOT_ACTIVE` (`details.required = ["finanzas"]`). Requires `finanzas:read`, `finanzas_reports:read` and `finanzas:export` (the Contador seat, owners and admins).
88639
+ */
88640
+ get: {
88641
+ parameters: {
88642
+ query?: {
88643
+ period?: string;
88644
+ };
88645
+ header?: never;
88646
+ path?: never;
88647
+ cookie?: never;
88648
+ };
88649
+ requestBody?: never;
88650
+ responses: {
88651
+ /** @description The manifest */
88652
+ 200: {
88653
+ headers: {
88654
+ [name: string]: unknown;
88655
+ };
88656
+ content: {
88657
+ /**
88658
+ * @example {
88659
+ * "data": {
88660
+ * "period": "2026-09",
88661
+ * "filename": "paquete-contador-2026-09.zip",
88662
+ * "files": [
88663
+ * {
88664
+ * "key": "libro_compras",
88665
+ * "label": "Libro de compras",
88666
+ * "filename": "01-libro-de-compras-2026-09.xlsx",
88667
+ * "rows": 84,
88668
+ * "size_bytes": 14210
88669
+ * },
88670
+ * {
88671
+ * "key": "libro_ventas",
88672
+ * "label": "Libro de ventas",
88673
+ * "filename": "02-libro-de-ventas-2026-09.xlsx",
88674
+ * "rows": 1312,
88675
+ * "size_bytes": 61904
88676
+ * },
88677
+ * {
88678
+ * "key": "honorarios",
88679
+ * "label": "Honorarios",
88680
+ * "filename": "03-honorarios-2026-09.xlsx",
88681
+ * "rows": 12,
88682
+ * "size_bytes": 7480
88683
+ * },
88684
+ * {
88685
+ * "key": "movimientos",
88686
+ * "label": "Movimientos por cuenta",
88687
+ * "filename": "04-movimientos-por-cuenta-2026-09.xlsx",
88688
+ * "rows": 640,
88689
+ * "size_bytes": 38112
88690
+ * },
88691
+ * {
88692
+ * "key": "clasificaciones",
88693
+ * "label": "Clasificaciones por cuenta",
88694
+ * "filename": "05-clasificaciones-por-cuenta-2026-09.xlsx",
88695
+ * "rows": 702,
88696
+ * "size_bytes": 40356
88697
+ * },
88698
+ * {
88699
+ * "key": "resultado",
88700
+ * "label": "Resultado",
88701
+ * "filename": "06-resultado-2026-09.xlsx",
88702
+ * "rows": 1398,
88703
+ * "size_bytes": 52018
88704
+ * },
88705
+ * {
88706
+ * "key": "ajustes",
88707
+ * "label": "Ajustes posteriores al cierre",
88708
+ * "filename": "07-ajustes-posteriores-al-cierre-2026-09.xlsx",
88709
+ * "rows": 3,
88710
+ * "size_bytes": 6920
88711
+ * }
88712
+ * ]
88713
+ * }
88714
+ * }
88715
+ */
88716
+ "application/json": {
88717
+ data: {
88718
+ /** @description The month, `YYYY-MM`. */
88719
+ period: string;
88720
+ /** @description The zip the download serves. */
88721
+ filename: string;
88722
+ files: {
88723
+ /** @enum {string} */
88724
+ key: "libro_compras" | "libro_ventas" | "honorarios" | "movimientos" | "clasificaciones" | "resultado" | "ajustes";
88725
+ label: string;
88726
+ /** @description The xlsx inside the zip. */
88727
+ filename: string;
88728
+ /** @description Rows of its «Detalle» sheet (documents, items) when it has one, else of its first sheet (movements, adjustments). */
88729
+ rows: number;
88730
+ size_bytes: number;
88731
+ }[];
88732
+ };
88733
+ };
88734
+ };
88735
+ };
88736
+ /** @description Validation error */
88737
+ 400: {
88738
+ headers: {
88739
+ [name: string]: unknown;
88740
+ };
88741
+ content: {
88742
+ "application/json": components["schemas"]["Error"];
88743
+ };
88744
+ };
88745
+ /** @description Unauthorized */
88746
+ 401: {
88747
+ headers: {
88748
+ [name: string]: unknown;
88749
+ };
88750
+ content: {
88751
+ "application/json": components["schemas"]["Error"];
88752
+ };
88753
+ };
88754
+ /** @description Forbidden */
88755
+ 403: {
88756
+ headers: {
88757
+ [name: string]: unknown;
88758
+ };
88759
+ content: {
88760
+ "application/json": components["schemas"]["Error"];
88761
+ };
88762
+ };
88763
+ /** @description Not found */
88764
+ 404: {
88765
+ headers: {
88766
+ [name: string]: unknown;
88767
+ };
88768
+ content: {
88769
+ "application/json": components["schemas"]["Error"];
88770
+ };
88771
+ };
88772
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
88773
+ 409: {
88774
+ headers: {
88775
+ [name: string]: unknown;
88776
+ };
88777
+ content: {
88778
+ "application/json": components["schemas"]["Error"];
88779
+ };
88780
+ };
88781
+ /** @description Rate limited */
88782
+ 429: {
88783
+ headers: {
88784
+ [name: string]: unknown;
88785
+ };
88786
+ content: {
88787
+ "application/json": components["schemas"]["Error"];
88788
+ };
88789
+ };
88790
+ };
88791
+ };
88792
+ put?: never;
88793
+ post?: never;
88794
+ delete?: never;
88795
+ options?: never;
88796
+ head?: never;
88797
+ patch?: never;
88798
+ trace?: never;
88799
+ };
88533
88800
  "/clinic/evidence-cards": {
88534
88801
  parameters: {
88535
88802
  query?: never;
@@ -139133,6 +139400,8 @@ interface paths {
139133
139400
  *
139134
139401
  * Each row also carries `campaign_origin` — the newest campaign send stored in the thread (`null` when none): its campaign, when it left, whether the send created the thread, and whether it is still the newest message (`awaiting_reply`). `fromCampaign=true` lists every such thread, closed ones included.
139135
139402
  *
139403
+ * Each row also carries `waiting_since` — when its customer started waiting on a human, `null` when nobody waits (the «Esperando respuesta» rule; see the `waiting` parameter). `waiting=true` lists only waiting conversations, team-wide; `sort=wait` orders urgent first, then the longest wait.
139404
+ *
139136
139405
  * Rows here do NOT carry `ad_origin_nudge` — that field is single-conversation-read only, to avoid a per-row integration lookup on every page of the inbox.
139137
139406
  *
139138
139407
  * `fields=summary` answers the SAME page with only the identifying and funnel scalars per row — `id`, `display_id`, `channel`, `source`, `status`, `handler`, `assignee_user_id`, `contact_id`, `ticket_id`, `awaiting_human_since`, `first_ai_message_at`, `last_message_date`, `created_at`, `ad_origin` — plus `contact_name`, `ticket_status` and `current_stage_id` off the embeds, and `assignee_name` / `unread` when the list computed them (an api_key has no read markers, so no `unread`). No `contact`, `ticket`, `lastMessage`, `tags`, `marketplaceWindow` or `last_call`. `meta` and `counts` are unchanged. A 20-row page is ~147k characters at `full` (the default, byte-identical to omitting the parameter) and a fraction of that at `summary`; page with `summary` to choose, then `GET /conversations/{id}` for one thread whole.
@@ -139164,7 +139433,10 @@ interface paths {
139164
139433
  aiReplied?: boolean | null;
139165
139434
  fromAd?: boolean | null;
139166
139435
  fromCampaign?: boolean | null;
139167
- sort?: "created_at" | "updated_at" | "last_message_date";
139436
+ /** @description true: only conversations where a customer is waiting on a human («Esperando respuesta»), team-wide. Waiting = open or pending, not filtered, on a channel you can reply in, and either an AI handoff nobody on the team has answered yet (a reply from ANY teammate clears it, including WhatsApp Business / Instagram app replies; a silent «Visita acordada» notice needs no answer) or a human-handled thread whose last message is the customer’s; waits older than 7 days are left out. Each row carries `waiting_since`. Pair with `sort=wait` for the queue order. */
139437
+ waiting?: boolean | null;
139438
+ /** @description `wait`: urgent (`handoff_urgent`) first, then by `waiting_since` — `order=desc` (default) is the longest wait first, `asc` the newest; conversations that are not waiting go last. */
139439
+ sort?: "created_at" | "updated_at" | "last_message_date" | "wait";
139168
139440
  order?: "asc" | "desc";
139169
139441
  include?: "counts";
139170
139442
  /** @description `full` (default): the inbox row with its `contact`, `ticket`, `lastMessage`, `tags`, `marketplaceWindow`, `ad_origin` and `last_call`, exactly as without the parameter. `summary`: only the identifying and funnel scalars — `id`, `display_id`, `channel`, `source`, `status`, `handler`, `assignee_user_id`, `contact_id`, `ticket_id`, `awaiting_human_since`, `first_ai_message_at`, `last_message_date`, `created_at`, `ad_origin`, plus `contact_name`, `ticket_status`, `current_stage_id` off the embeds and `assignee_name` / `unread` when the list computed them. Pagination, `meta` and `counts` are unchanged. Use `summary` to page the inbox cheaply, then `GET /conversations/{id}` for one thread in full. */
@@ -139303,7 +139575,8 @@ interface paths {
139303
139575
  * }
139304
139576
  * ],
139305
139577
  * "last_call": null,
139306
- * "handoff_urgent": false
139578
+ * "handoff_urgent": false,
139579
+ * "waiting_since": null
139307
139580
  * },
139308
139581
  * {
139309
139582
  * "id": "bbbbbbbb-0000-4000-8000-000000000002",
@@ -139440,7 +139713,8 @@ interface paths {
139440
139713
  * },
139441
139714
  * "tags": [],
139442
139715
  * "last_call": null,
139443
- * "handoff_urgent": false
139716
+ * "handoff_urgent": false,
139717
+ * "waiting_since": null
139444
139718
  * }
139445
139719
  * ],
139446
139720
  * "meta": {
@@ -139729,9 +140003,11 @@ interface paths {
139729
140003
  cookie?: never;
139730
140004
  };
139731
140005
  /**
139732
- * Inbox badge counts (my open / unread / mentions / unassigned)
140006
+ * Inbox badge counts (my open / unread / mentions / unassigned / waiting)
139733
140007
  * @description The four badge counts on their own, scoped to the calling principal: `myOpen` and `myUnread` are the conversations assigned to the member behind the credential, `myMentions` the ones that @-mention them, `unassigned` the whole workspace’s unowned queue. An API key has no member behind it, so its three "my" counts read 0 and only `unassigned` is meaningful — use a personal token to get a person’s badges.
139734
140008
  *
140009
+ * «Esperando respuesta» (the waiting queue): `waiting` is how many conversations have a customer waiting on a human, team-wide (the list `GET /conversations?waiting=true` returns exactly these); `waitingUrgent` how many of them are flagged urgent; `waitingMine` how many are assigned to the caller (0 for an API key); `waitingOldestSince` when the longest wait started (ISO, `null` when nobody waits) — render it as «el más antiguo 2 h».
140010
+ *
139735
140011
  * `GET /conversations?include=counts` returns the same payload beside a page of conversations; this route exists for the caller that wants the badges without one.
139736
140012
  */
139737
140013
  get: {
@@ -139755,7 +140031,11 @@ interface paths {
139755
140031
  * "myOpen": 3,
139756
140032
  * "myUnread": 1,
139757
140033
  * "unassigned": 2,
139758
- * "myMentions": 0
140034
+ * "myMentions": 0,
140035
+ * "waiting": 3,
140036
+ * "waitingUrgent": 1,
140037
+ * "waitingMine": 1,
140038
+ * "waitingOldestSince": "2026-10-31T13:05:00.000Z"
139759
140039
  * }
139760
140040
  * }
139761
140041
  */
@@ -139994,6 +140274,7 @@ interface paths {
139994
140274
  * ],
139995
140275
  * "last_call": null,
139996
140276
  * "handoff_urgent": false,
140277
+ * "waiting_since": null,
139997
140278
  * "ad_origin_nudge": null,
139998
140279
  * "handoff_resolved_suggested": false,
139999
140280
  * "message": [
@@ -237685,6 +237966,8 @@ interface paths {
237685
237966
  * This car’s lead timeline
237686
237967
  * @description Every named lead that asked about this vehicle, merged with its anonymous Yapo phone-reveals, newest first. This is the car’s HISTORY, so unlike `/interest` a sold vehicle keeps its timeline.
237687
237968
  *
237969
+ * Each lead row carries `conversation_id`: the chat to open for that person (the lead's own conversation, else the contact's most recent one, e.g. the first-touch WhatsApp after a portal inquiry). Null when there is none.
237970
+ *
237688
237971
  * The Stock detail screen merges this with `/vehicles/{id}/history` (the change log) into one "Leads y cambios" feed — they are two endpoints because they are two different kinds of event. Automotive-vertical only: a workspace on another vertical gets 403 here however its scopes are set.
237689
237972
  */
237690
237973
  get: {
@@ -237713,6 +237996,7 @@ interface paths {
237713
237996
  * "kind": "lead",
237714
237997
  * "contact_id": "22222222-0000-4000-8000-000000000001",
237715
237998
  * "lead_id": "99999999-0000-4000-8000-000000000002",
237999
+ * "conversation_id": "99999999-0000-4000-8000-000000000003",
237716
238000
  * "portal": "website",
237717
238001
  * "at": "2026-09-19T15:30:00.000Z"
237718
238002
  * }
@@ -254580,7 +254864,7 @@ interface components {
254580
254864
  perfect_battery_pct?: number;
254581
254865
  currency?: string;
254582
254866
  };
254583
- /** @description Workspace notification rails. The staff WhatsApp rails (`staff_whatsapp`, `unanswered_supervisor_whatsapp`, `booking_whatsapp`) are opt-in: arming one (`enabled: true` with an `account_id`) submits its template to Meta on that number and wires it on approval. Each rail sent replaces that rail whole; rails not sent are kept. */
254867
+ /** @description Workspace notification rails. The staff WhatsApp rails (`staff_whatsapp`, `unanswered_supervisor_whatsapp`, `booking_whatsapp`, `reminder_whatsapp`) are opt-in: arming one (`enabled: true` with an `account_id`) submits its template to Meta on that number and wires it on approval. Each rail sent replaces that rail whole; rails not sent are kept. */
254584
254868
  notifications?: {
254585
254869
  staff_whatsapp?: {
254586
254870
  enabled?: boolean;
@@ -254596,6 +254880,16 @@ interface components {
254596
254880
  template_name?: string;
254597
254881
  template_language?: string;
254598
254882
  user_ids?: string[];
254883
+ /** @description Minutes of open time a conversation waits on the team before the supervisors get the WhatsApp, once per episode. Unset = 60. */
254884
+ delay_minutes?: number;
254885
+ };
254886
+ /** @description Staff WhatsApp on every «sin respuesta» reminder, to the assignee (template aviso_sigue_esperando_v1). Opt-in: each send costs a WhatsApp conversation. */
254887
+ reminder_whatsapp?: {
254888
+ enabled?: boolean;
254889
+ /** Format: uuid */
254890
+ account_id?: string;
254891
+ template_name?: string;
254892
+ template_language?: string;
254599
254893
  };
254600
254894
  booking_whatsapp?: {
254601
254895
  enabled?: boolean;
@@ -254672,6 +254966,26 @@ interface components {
254672
254966
  rejection_reason: string | null;
254673
254967
  }[];
254674
254968
  };
254969
+ reminder_whatsapp: {
254970
+ /** @description The rail is switched on (`enabled: true`). */
254971
+ armed: boolean;
254972
+ /**
254973
+ * @description Worst state across the rail templates (blocked, rejected, missing, pending, approved); `off` when not armed or nothing to provision; `no_number` when armed without a sending number.
254974
+ * @enum {string}
254975
+ */
254976
+ template_state: "off" | "no_number" | "missing" | "pending" | "approved" | "rejected" | "blocked";
254977
+ reason: string | null;
254978
+ /** @description The rail has its template name set (by an admin or on approval). */
254979
+ wired: boolean;
254980
+ templates: {
254981
+ key: string;
254982
+ name: string;
254983
+ language: string;
254984
+ /** @enum {string} */
254985
+ state: "missing" | "pending" | "approved" | "rejected" | "blocked";
254986
+ rejection_reason: string | null;
254987
+ }[];
254988
+ };
254675
254989
  };
254676
254990
  /** @description Workspace display name. */
254677
254991
  name?: string;
@@ -254884,6 +255198,16 @@ interface components {
254884
255198
  template_name?: string;
254885
255199
  template_language?: string;
254886
255200
  user_ids?: string[];
255201
+ /** @description Minutes of open time a conversation waits on the team before the supervisors get the WhatsApp, once per episode. Unset = 60. */
255202
+ delay_minutes?: number;
255203
+ };
255204
+ /** @description Staff WhatsApp on every «sin respuesta» reminder, to the assignee (template aviso_sigue_esperando_v1). Opt-in: each send costs a WhatsApp conversation. */
255205
+ reminder_whatsapp?: {
255206
+ enabled?: boolean;
255207
+ /** Format: uuid */
255208
+ account_id?: string;
255209
+ template_name?: string;
255210
+ template_language?: string;
254887
255211
  };
254888
255212
  booking_whatsapp?: {
254889
255213
  enabled?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitrina/api",
3
- "version": "15.16.0",
3
+ "version": "15.18.0",
4
4
  "description": "Typed TypeScript SDK for the Vitrina API pública — generated from the committed openapi.public.json (ADR 0106).",
5
5
  "type": "module",
6
6
  "license": "MIT",