@vitrina/api 13.6.0 → 13.7.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.ts CHANGED
@@ -140946,6 +140946,77 @@ interface paths {
140946
140946
  [name: string]: unknown;
140947
140947
  };
140948
140948
  content: {
140949
+ /**
140950
+ * @example {
140951
+ * "data": {
140952
+ * "level": "arm",
140953
+ * "basis": "cohort",
140954
+ * "window": {
140955
+ * "from": "2026-11-16",
140956
+ * "to": "2026-11-29"
140957
+ * },
140958
+ * "rows": [
140959
+ * {
140960
+ * "arm": "whatsapp",
140961
+ * "external_id": null,
140962
+ * "name": null,
140963
+ * "parent": null,
140964
+ * "unattributed": false,
140965
+ * "leads": 100,
140966
+ * "qualified": 50,
140967
+ * "booked": 25,
140968
+ * "held": 12,
140969
+ * "paid": 0,
140970
+ * "contacted_24h": 90,
140971
+ * "spend": 1400000,
140972
+ * "costs": {
140973
+ * "per_lead": 14000,
140974
+ * "per_qualified": 28000,
140975
+ * "per_booked": 56000,
140976
+ * "per_held": 112000,
140977
+ * "per_paid": null
140978
+ * },
140979
+ * "rates": {
140980
+ * "qualified_share": 0.5,
140981
+ * "contacted_24h_rate": 0.9,
140982
+ * "lead_to_booked": 0.25,
140983
+ * "show_rate": 0.5
140984
+ * }
140985
+ * }
140986
+ * ],
140987
+ * "arms": [],
140988
+ * "totals": {
140989
+ * "leads": 100,
140990
+ * "qualified": 50,
140991
+ * "booked": 25,
140992
+ * "held": 12,
140993
+ * "paid": 0,
140994
+ * "contacted_24h": 90,
140995
+ * "spend": 1400000,
140996
+ * "costs": {
140997
+ * "per_lead": 14000,
140998
+ * "per_qualified": 28000,
140999
+ * "per_booked": 56000,
141000
+ * "per_held": 112000,
141001
+ * "per_paid": null
141002
+ * },
141003
+ * "rates": {
141004
+ * "qualified_share": 0.5,
141005
+ * "contacted_24h_rate": 0.9,
141006
+ * "lead_to_booked": 0.25,
141007
+ * "show_rate": 0.5
141008
+ * }
141009
+ * },
141010
+ * "reconciliation": {
141011
+ * "overview_spend": 1400000,
141012
+ * "spend_matches": true,
141013
+ * "events": null,
141014
+ * "counts_match": null,
141015
+ * "leads_without_ad": 0
141016
+ * }
141017
+ * }
141018
+ * }
141019
+ */
140949
141020
  "application/json": {
140950
141021
  data: components["schemas"]["AdsDemoFunnel"];
140951
141022
  };
@@ -146229,6 +146300,43 @@ interface paths {
146229
146300
  * "dismissed_until": null
146230
146301
  * },
146231
146302
  * {
146303
+ * "key": "budget_change:120210000000000002:2026-09-23",
146304
+ * "kind": "budget_change",
146305
+ * "screen": [
146306
+ * "campanas",
146307
+ * "resumen"
146308
+ * ],
146309
+ * "primary": false,
146310
+ * "title": "Bajar 20 % el presupuesto de la campaña «Blanqueamiento · Octubre»",
146311
+ * "why": "Se lleva el **46 %** del gasto de tus campañas (**$412.000** en septiembre) y agenda el **2,1 %** (3 de 140); «Implantes · Septiembre» agenda el **9,8 %** (12 de 122). La campaña reparte su presupuesto sola, así que se baja ahí: de **$25.000** a **$20.000** al día; 20 % es el paso máximo, y el siguiente, en 3 días.",
146312
+ * "effect": "El presupuesto diario de la campaña baja 20 %; puedes deshacerlo.",
146313
+ * "target": {
146314
+ * "level": "campaign",
146315
+ * "external_id": "120210000000000002",
146316
+ * "name": "Blanqueamiento · Octubre",
146317
+ * "campaign_external_id": null,
146318
+ * "campaign": {
146319
+ * "external_id": "120210000000000002",
146320
+ * "name": "Blanqueamiento · Octubre"
146321
+ * }
146322
+ * },
146323
+ * "params": {
146324
+ * "budget_change_pct": -20,
146325
+ * "budget_level": "campaign",
146326
+ * "budget_type": "daily",
146327
+ * "daily_budget": 25000,
146328
+ * "new_daily_budget": 20000,
146329
+ * "step_pct": 20
146330
+ * },
146331
+ * "reversible": true,
146332
+ * "risk": "reversible",
146333
+ * "engine_ref": {
146334
+ * "op": "campaigns.budget"
146335
+ * },
146336
+ * "facts": [],
146337
+ * "dismissed_until": null
146338
+ * },
146339
+ * {
146232
146340
  * "key": "nav:cheapest:120210000000000002:2026-09-23",
146233
146341
  * "kind": "nav",
146234
146342
  * "screen": [
@@ -150430,7 +150538,10 @@ interface paths {
150430
150538
  * "data": {
150431
150539
  * "offer": {
150432
150540
  * "available": false,
150433
- * "reason": "already_open"
150541
+ * "reason": "already_open",
150542
+ * "suggested_test_event": "appointment_booked",
150543
+ * "weekly_first_payments": 12,
150544
+ * "first_payment_threshold": 50
150434
150545
  * },
150435
150546
  * "experiments": [
150436
150547
  * {
@@ -150578,7 +150689,7 @@ interface paths {
150578
150689
  put?: never;
150579
150690
  /**
150580
150691
  * Record an optimisation experiment (draft)
150581
- * @description Records the control (today’s «conversaciones» ad sets) and the test arm (ad sets optimising on `test_event`, default the first payment). No Meta write: Meta’s API cannot re-optimise a published click-to-WhatsApp ad set on a purchase (the Sales objective belongs to the campaign and a published ad set’s optimisation is frozen), so the test arm is built in Ads Manager and its ids recorded here (now or with PATCH). 409 when not offered (`details.reason`).
150692
+ * @description Records the control (today’s «conversaciones» ad sets) and the test arm (ad sets optimising on `test_event`; default, D81: the offer’s `suggested_test_event` — «cita agendada» until ~50 first payments a week, then the first payment). No Meta write: Meta’s API cannot re-optimise a published click-to-WhatsApp ad set on a purchase (the Sales objective belongs to the campaign and a published ad set’s optimisation is frozen), so the test arm is built in Ads Manager and its ids recorded here (now or with PATCH). 409 when not offered (`details.reason`).
150582
150693
  */
150583
150694
  post: {
150584
150695
  parameters: {
@@ -151604,7 +151715,7 @@ interface paths {
151604
151715
  patch?: never;
151605
151716
  trace?: never;
151606
151717
  };
151607
- "/ads/audiences/customer-list": {
151718
+ "/ads/leadgen-forms": {
151608
151719
  parameters: {
151609
151720
  query?: never;
151610
151721
  header?: never;
@@ -151612,8 +151723,8 @@ interface paths {
151612
151723
  cookie?: never;
151613
151724
  };
151614
151725
  /**
151615
- * The paying-customers audience: state and consent
151616
- * @description A Meta customer list named «Vitrina · Clientes que pagaron»: the people who paid in the last year, as hashed e-mail, phone, first name, last name and country (never RUT, amounts, treatments or clinical data; a paying guardian is sent as the guardian), marketing opt-outs and minors with a known birth date always excluded, refreshed daily by the measurement engine; usable in Meta for a 1 % lookalike (optional), exclusion or retargeting. Google Customer Match is never used for clinics. The read is also the capability check: `coming_soon` until the engine can build it.
151726
+ * Instant Form mappings
151727
+ * @description The Meta Instant Forms this workspace mapped, and the built-in presets. A form with no entry uses the default mapping (Meta’s standard fields — name, phone, email, company — and every other answer kept in the lead note) and the inbox intake. Submissions arrive from Atribu once Meta approves lead access for the app.
151617
151728
  */
151618
151729
  get: {
151619
151730
  parameters: {
@@ -151624,7 +151735,7 @@ interface paths {
151624
151735
  };
151625
151736
  requestBody?: never;
151626
151737
  responses: {
151627
- /** @description The audience state. */
151738
+ /** @description The forms and presets. */
151628
151739
  200: {
151629
151740
  headers: {
151630
151741
  [name: string]: unknown;
@@ -151633,37 +151744,685 @@ interface paths {
151633
151744
  /**
151634
151745
  * @example {
151635
151746
  * "data": {
151636
- * "state": "live",
151637
- * "reason": null,
151638
- * "names": {
151639
- * "audience": "Vitrina · Clientes que pagaron",
151640
- * "lookalike": "Vitrina · Parecidos a clientes que pagaron (1 %)"
151641
- * },
151642
- * "size": {
151643
- * "lower": 1000,
151644
- * "upper": 1200
151645
- * },
151646
- * "last_refreshed_at": "2026-10-03T06:40:00.000Z",
151647
- * "lookalike": {
151648
- * "state": "ready",
151649
- * "ratio": 0.01
151650
- * },
151651
- * "optin": {
151652
- * "accepted_at": "2026-10-01T21:10:00.000Z",
151653
- * "consent_version": "2026-10-04.customer-list.v2",
151654
- * "current": true,
151655
- * "lookalike": true
151656
- * },
151657
- * "consent": {
151658
- * "version": "2026-10-04.customer-list.v2",
151659
- * "text": "Vitrina creará en tu cuenta de Meta una lista llamada «Vitrina · Clientes que pagaron»…",
151660
- * "text_hash": "0000000000000000000000000000000000000000000000000000000000000000"
151747
+ * "forms": [
151748
+ * {
151749
+ * "form_id": "1234567890123456",
151750
+ * "name": "Vitrina · Automotoras · Higher intent v1",
151751
+ * "intake": "demo",
151752
+ * "enabled": true,
151753
+ * "mapping": {
151754
+ * "vertical": "automotora",
151755
+ * "rules": []
151756
+ * },
151757
+ * "updated_at": "2026-10-11T15:00:00.000Z"
151758
+ * }
151759
+ * ],
151760
+ * "presets": [
151761
+ * "vitrina_automotora_v1"
151762
+ * ]
151763
+ * }
151764
+ * }
151765
+ */
151766
+ "application/json": {
151767
+ data: {
151768
+ forms: {
151769
+ form_id: string;
151770
+ name: string | null;
151771
+ /** @enum {string} */
151772
+ intake: "inbox" | "demo";
151773
+ enabled: boolean;
151774
+ mapping: {
151775
+ [key: string]: unknown;
151776
+ };
151777
+ updated_at: string;
151778
+ }[];
151779
+ presets: string[];
151780
+ };
151781
+ };
151782
+ };
151783
+ };
151784
+ /** @description Validation error */
151785
+ 400: {
151786
+ headers: {
151787
+ [name: string]: unknown;
151788
+ };
151789
+ content: {
151790
+ "application/json": components["schemas"]["Error"];
151791
+ };
151792
+ };
151793
+ /** @description Unauthorized */
151794
+ 401: {
151795
+ headers: {
151796
+ [name: string]: unknown;
151797
+ };
151798
+ content: {
151799
+ "application/json": components["schemas"]["Error"];
151800
+ };
151801
+ };
151802
+ /** @description Vitrina Ads is not active for this workspace. `error.code` is `ENTITLEMENT_NOT_ACTIVE`, `error.details.feature` is `vitrina_ads`. */
151803
+ 402: {
151804
+ headers: {
151805
+ [name: string]: unknown;
151806
+ };
151807
+ content: {
151808
+ /**
151809
+ * @example {
151810
+ * "error": {
151811
+ * "code": "ENTITLEMENT_NOT_ACTIVE",
151812
+ * "message": "El complemento Vitrina Ads no está activo en este espacio de trabajo.",
151813
+ * "details": {
151814
+ * "required": [
151815
+ * "vitrina_ads"
151816
+ * ],
151817
+ * "active": [],
151818
+ * "feature": "vitrina_ads",
151819
+ * "entitlement_state": "off"
151661
151820
  * }
151662
151821
  * }
151663
151822
  * }
151664
151823
  */
151824
+ "application/json": components["schemas"]["Error"];
151825
+ };
151826
+ };
151827
+ /** @description Forbidden */
151828
+ 403: {
151829
+ headers: {
151830
+ [name: string]: unknown;
151831
+ };
151832
+ content: {
151833
+ "application/json": components["schemas"]["Error"];
151834
+ };
151835
+ };
151836
+ /** @description Not found */
151837
+ 404: {
151838
+ headers: {
151839
+ [name: string]: unknown;
151840
+ };
151841
+ content: {
151842
+ "application/json": components["schemas"]["Error"];
151843
+ };
151844
+ };
151845
+ /** @description Vitrina Ads is active but the delegated key has not finished rotating (`ADS_KEY_NEEDS_REMINT`) — retry once `GET /ads/state` reports `needs_remint: false`. Also the generic conflict of a write. */
151846
+ 409: {
151847
+ headers: {
151848
+ [name: string]: unknown;
151849
+ };
151850
+ content: {
151851
+ /**
151852
+ * @example {
151853
+ * "error": {
151854
+ * "code": "ADS_KEY_NEEDS_REMINT",
151855
+ * "message": "Vitrina Ads is active but the delegated key has not finished rotating (key_kind: core)",
151856
+ * "details": {
151857
+ * "key_kind": "core"
151858
+ * }
151859
+ * }
151860
+ * }
151861
+ */
151862
+ "application/json": components["schemas"]["Error"];
151863
+ };
151864
+ };
151865
+ /** @description Rate limited */
151866
+ 429: {
151867
+ headers: {
151868
+ [name: string]: unknown;
151869
+ };
151870
+ content: {
151871
+ "application/json": components["schemas"]["Error"];
151872
+ };
151873
+ };
151874
+ };
151875
+ };
151876
+ put?: never;
151877
+ post?: never;
151878
+ delete?: never;
151879
+ options?: never;
151880
+ head?: never;
151881
+ patch?: never;
151882
+ trace?: never;
151883
+ };
151884
+ "/ads/leadgen-forms/{form_id}": {
151885
+ parameters: {
151886
+ query?: never;
151887
+ header?: never;
151888
+ path?: never;
151889
+ cookie?: never;
151890
+ };
151891
+ get?: never;
151892
+ /**
151893
+ * Map an Instant Form
151894
+ * @description Creates or replaces the mapping of one Instant Form: which question is the name, phone, email and company, how fit answers are read, which checkbox is the marketing opt-in and whether contacting the person is allowed by the submission itself (`contact_consent`). `intake: demo` feeds the founders’ demo intake (409 `no_demo_booking` for a workspace that takes none). A missing answer never blocks the lead.
151895
+ */
151896
+ put: {
151897
+ parameters: {
151898
+ query?: never;
151899
+ header?: never;
151900
+ path: {
151901
+ /** @description The Instant Form's id in Meta (`form_id` of the lead). */
151902
+ form_id: string;
151903
+ };
151904
+ cookie?: never;
151905
+ };
151906
+ requestBody?: {
151907
+ content: {
151908
+ /**
151909
+ * @example {
151910
+ * "name": "Vitrina · Automotoras · Higher intent v1",
151911
+ * "intake": "demo",
151912
+ * "preset": "vitrina_automotora_v1"
151913
+ * }
151914
+ */
151915
+ "application/json": {
151916
+ name?: string;
151917
+ /**
151918
+ * @description `inbox`: a contact, a lead and an inbox conversation. `demo`: the form feeds the founders’ demo intake (only workspaces that take demo bookings): fit score, Lead / QualifiedLead events and the 7-day follow-up.
151919
+ * @default inbox
151920
+ * @enum {string}
151921
+ */
151922
+ intake?: "inbox" | "demo";
151923
+ /**
151924
+ * @description A built-in mapping (`vitrina_automotora_v1`: the Vitrina «Automotoras» form). Replaces `mapping`.
151925
+ * @enum {string}
151926
+ */
151927
+ preset?: "vitrina_automotora_v1";
151928
+ /** @description Per-question rules. A rule names the Meta question (`source`, compared lowercase / without accents / with `_` for spaces; `aliases` add more spellings), where its answer goes (`target`: contact.name, contact.first_name, contact.last_name, contact.phone, contact.email, contact.company, qualification.role, qualification.size, qualification.sellers, note), and optionally `values` (normalised answer → stored value). Every answer is kept in the lead note whatever its rule; a missing answer never blocks the lead. */
151929
+ mapping?: {
151930
+ /** @default [] */
151931
+ rules?: {
151932
+ source: string;
151933
+ aliases?: string[];
151934
+ /** @enum {string} */
151935
+ target: "contact.name" | "contact.first_name" | "contact.last_name" | "contact.phone" | "contact.email" | "contact.company" | "qualification.role" | "qualification.size" | "qualification.sellers" | "note";
151936
+ label?: string;
151937
+ values?: {
151938
+ [key: string]: string;
151939
+ };
151940
+ }[];
151941
+ marketing_checkbox_key?: string;
151942
+ notice_version?: string;
151943
+ marketing_text_version?: string;
151944
+ /** @enum {string} */
151945
+ contact_consent?: "none" | "submission" | "disclaimer";
151946
+ /** @enum {string} */
151947
+ vertical?: "automotora" | "clinica" | "otro";
151948
+ };
151949
+ /** @default true */
151950
+ enabled?: boolean;
151951
+ };
151952
+ };
151953
+ };
151954
+ responses: {
151955
+ /** @description The stored mapping. */
151956
+ 200: {
151957
+ headers: {
151958
+ [name: string]: unknown;
151959
+ };
151960
+ content: {
151961
+ /**
151962
+ * @example {
151963
+ * "data": {
151964
+ * "form_id": "1234567890123456",
151965
+ * "name": "Vitrina · Automotoras · Higher intent v1",
151966
+ * "intake": "demo",
151967
+ * "enabled": true,
151968
+ * "mapping": {
151969
+ * "vertical": "automotora",
151970
+ * "rules": []
151971
+ * },
151972
+ * "updated_at": "2026-10-11T15:00:00.000Z"
151973
+ * }
151974
+ * }
151975
+ */
151665
151976
  "application/json": {
151666
- data: components["schemas"]["AdsAudienceStatus"];
151977
+ data: {
151978
+ form_id: string;
151979
+ name: string | null;
151980
+ /** @enum {string} */
151981
+ intake: "inbox" | "demo";
151982
+ enabled: boolean;
151983
+ mapping: {
151984
+ [key: string]: unknown;
151985
+ };
151986
+ updated_at: string;
151987
+ };
151988
+ };
151989
+ };
151990
+ };
151991
+ /** @description Validation error */
151992
+ 400: {
151993
+ headers: {
151994
+ [name: string]: unknown;
151995
+ };
151996
+ content: {
151997
+ "application/json": components["schemas"]["Error"];
151998
+ };
151999
+ };
152000
+ /** @description Unauthorized */
152001
+ 401: {
152002
+ headers: {
152003
+ [name: string]: unknown;
152004
+ };
152005
+ content: {
152006
+ "application/json": components["schemas"]["Error"];
152007
+ };
152008
+ };
152009
+ /** @description Vitrina Ads is not active for this workspace. `error.code` is `ENTITLEMENT_NOT_ACTIVE`, `error.details.feature` is `vitrina_ads`. */
152010
+ 402: {
152011
+ headers: {
152012
+ [name: string]: unknown;
152013
+ };
152014
+ content: {
152015
+ /**
152016
+ * @example {
152017
+ * "error": {
152018
+ * "code": "ENTITLEMENT_NOT_ACTIVE",
152019
+ * "message": "El complemento Vitrina Ads no está activo en este espacio de trabajo.",
152020
+ * "details": {
152021
+ * "required": [
152022
+ * "vitrina_ads"
152023
+ * ],
152024
+ * "active": [],
152025
+ * "feature": "vitrina_ads",
152026
+ * "entitlement_state": "off"
152027
+ * }
152028
+ * }
152029
+ * }
152030
+ */
152031
+ "application/json": components["schemas"]["Error"];
152032
+ };
152033
+ };
152034
+ /** @description Forbidden */
152035
+ 403: {
152036
+ headers: {
152037
+ [name: string]: unknown;
152038
+ };
152039
+ content: {
152040
+ "application/json": components["schemas"]["Error"];
152041
+ };
152042
+ };
152043
+ /** @description Not found */
152044
+ 404: {
152045
+ headers: {
152046
+ [name: string]: unknown;
152047
+ };
152048
+ content: {
152049
+ "application/json": components["schemas"]["Error"];
152050
+ };
152051
+ };
152052
+ /** @description Vitrina Ads is active but the delegated key has not finished rotating (`ADS_KEY_NEEDS_REMINT`) — retry once `GET /ads/state` reports `needs_remint: false`. Also the generic conflict of a write. */
152053
+ 409: {
152054
+ headers: {
152055
+ [name: string]: unknown;
152056
+ };
152057
+ content: {
152058
+ /**
152059
+ * @example {
152060
+ * "error": {
152061
+ * "code": "ADS_KEY_NEEDS_REMINT",
152062
+ * "message": "Vitrina Ads is active but the delegated key has not finished rotating (key_kind: core)",
152063
+ * "details": {
152064
+ * "key_kind": "core"
152065
+ * }
152066
+ * }
152067
+ * }
152068
+ */
152069
+ "application/json": components["schemas"]["Error"];
152070
+ };
152071
+ };
152072
+ /** @description Rate limited */
152073
+ 429: {
152074
+ headers: {
152075
+ [name: string]: unknown;
152076
+ };
152077
+ content: {
152078
+ "application/json": components["schemas"]["Error"];
152079
+ };
152080
+ };
152081
+ };
152082
+ };
152083
+ post?: never;
152084
+ /**
152085
+ * Remove an Instant Form mapping
152086
+ * @description Back to the default mapping and the inbox intake for that form. Leads already created are untouched.
152087
+ */
152088
+ delete: {
152089
+ parameters: {
152090
+ query?: never;
152091
+ header?: never;
152092
+ path: {
152093
+ /** @description The Instant Form's id in Meta (`form_id` of the lead). */
152094
+ form_id: string;
152095
+ };
152096
+ cookie?: never;
152097
+ };
152098
+ requestBody?: never;
152099
+ responses: {
152100
+ /** @description Removed. */
152101
+ 204: {
152102
+ headers: {
152103
+ [name: string]: unknown;
152104
+ };
152105
+ content?: never;
152106
+ };
152107
+ /** @description Validation error */
152108
+ 400: {
152109
+ headers: {
152110
+ [name: string]: unknown;
152111
+ };
152112
+ content: {
152113
+ "application/json": components["schemas"]["Error"];
152114
+ };
152115
+ };
152116
+ /** @description Unauthorized */
152117
+ 401: {
152118
+ headers: {
152119
+ [name: string]: unknown;
152120
+ };
152121
+ content: {
152122
+ "application/json": components["schemas"]["Error"];
152123
+ };
152124
+ };
152125
+ /** @description Vitrina Ads is not active for this workspace. `error.code` is `ENTITLEMENT_NOT_ACTIVE`, `error.details.feature` is `vitrina_ads`. */
152126
+ 402: {
152127
+ headers: {
152128
+ [name: string]: unknown;
152129
+ };
152130
+ content: {
152131
+ /**
152132
+ * @example {
152133
+ * "error": {
152134
+ * "code": "ENTITLEMENT_NOT_ACTIVE",
152135
+ * "message": "El complemento Vitrina Ads no está activo en este espacio de trabajo.",
152136
+ * "details": {
152137
+ * "required": [
152138
+ * "vitrina_ads"
152139
+ * ],
152140
+ * "active": [],
152141
+ * "feature": "vitrina_ads",
152142
+ * "entitlement_state": "off"
152143
+ * }
152144
+ * }
152145
+ * }
152146
+ */
152147
+ "application/json": components["schemas"]["Error"];
152148
+ };
152149
+ };
152150
+ /** @description Forbidden */
152151
+ 403: {
152152
+ headers: {
152153
+ [name: string]: unknown;
152154
+ };
152155
+ content: {
152156
+ "application/json": components["schemas"]["Error"];
152157
+ };
152158
+ };
152159
+ /** @description Not found */
152160
+ 404: {
152161
+ headers: {
152162
+ [name: string]: unknown;
152163
+ };
152164
+ content: {
152165
+ "application/json": components["schemas"]["Error"];
152166
+ };
152167
+ };
152168
+ /** @description Vitrina Ads is active but the delegated key has not finished rotating (`ADS_KEY_NEEDS_REMINT`) — retry once `GET /ads/state` reports `needs_remint: false`. Also the generic conflict of a write. */
152169
+ 409: {
152170
+ headers: {
152171
+ [name: string]: unknown;
152172
+ };
152173
+ content: {
152174
+ /**
152175
+ * @example {
152176
+ * "error": {
152177
+ * "code": "ADS_KEY_NEEDS_REMINT",
152178
+ * "message": "Vitrina Ads is active but the delegated key has not finished rotating (key_kind: core)",
152179
+ * "details": {
152180
+ * "key_kind": "core"
152181
+ * }
152182
+ * }
152183
+ * }
152184
+ */
152185
+ "application/json": components["schemas"]["Error"];
152186
+ };
152187
+ };
152188
+ /** @description Rate limited */
152189
+ 429: {
152190
+ headers: {
152191
+ [name: string]: unknown;
152192
+ };
152193
+ content: {
152194
+ "application/json": components["schemas"]["Error"];
152195
+ };
152196
+ };
152197
+ };
152198
+ };
152199
+ options?: never;
152200
+ head?: never;
152201
+ patch?: never;
152202
+ trace?: never;
152203
+ };
152204
+ "/ads/leadgen-submissions": {
152205
+ parameters: {
152206
+ query?: never;
152207
+ header?: never;
152208
+ path?: never;
152209
+ cookie?: never;
152210
+ };
152211
+ /**
152212
+ * Latest Instant Form submissions
152213
+ * @description The latest submissions received, newest first: whether each was applied (`processed`), is being applied (`received`) or failed and will be retried (`failed`, with the error), which intake took it, and the lead and contact it produced. No answers.
152214
+ */
152215
+ get: {
152216
+ parameters: {
152217
+ query?: {
152218
+ limit?: number;
152219
+ };
152220
+ header?: never;
152221
+ path?: never;
152222
+ cookie?: never;
152223
+ };
152224
+ requestBody?: never;
152225
+ responses: {
152226
+ /** @description The submissions. */
152227
+ 200: {
152228
+ headers: {
152229
+ [name: string]: unknown;
152230
+ };
152231
+ content: {
152232
+ /**
152233
+ * @example {
152234
+ * "data": {
152235
+ * "submissions": [
152236
+ * {
152237
+ * "leadgen_id": "444444444444",
152238
+ * "status": "processed",
152239
+ * "route": "demo",
152240
+ * "attempts": 1,
152241
+ * "lead_id": "01926f3e-7c1a-7b2e-9f10-3a5b7c9d0e1f",
152242
+ * "contact_id": "01926f3e-7c1a-7b2e-9f10-3a5b7c9d0e20",
152243
+ * "error": null
152244
+ * }
152245
+ * ]
152246
+ * }
152247
+ * }
152248
+ */
152249
+ "application/json": {
152250
+ data: {
152251
+ submissions: {
152252
+ leadgen_id: string;
152253
+ /** @enum {string} */
152254
+ status: "received" | "processed" | "failed";
152255
+ /** @enum {string|null} */
152256
+ route: "demo" | "inbox" | "inbox_fallback" | null;
152257
+ attempts: number;
152258
+ lead_id: string | null;
152259
+ contact_id: string | null;
152260
+ error: string | null;
152261
+ }[];
152262
+ };
152263
+ };
152264
+ };
152265
+ };
152266
+ /** @description Validation error */
152267
+ 400: {
152268
+ headers: {
152269
+ [name: string]: unknown;
152270
+ };
152271
+ content: {
152272
+ "application/json": components["schemas"]["Error"];
152273
+ };
152274
+ };
152275
+ /** @description Unauthorized */
152276
+ 401: {
152277
+ headers: {
152278
+ [name: string]: unknown;
152279
+ };
152280
+ content: {
152281
+ "application/json": components["schemas"]["Error"];
152282
+ };
152283
+ };
152284
+ /** @description Vitrina Ads is not active for this workspace. `error.code` is `ENTITLEMENT_NOT_ACTIVE`, `error.details.feature` is `vitrina_ads`. */
152285
+ 402: {
152286
+ headers: {
152287
+ [name: string]: unknown;
152288
+ };
152289
+ content: {
152290
+ /**
152291
+ * @example {
152292
+ * "error": {
152293
+ * "code": "ENTITLEMENT_NOT_ACTIVE",
152294
+ * "message": "El complemento Vitrina Ads no está activo en este espacio de trabajo.",
152295
+ * "details": {
152296
+ * "required": [
152297
+ * "vitrina_ads"
152298
+ * ],
152299
+ * "active": [],
152300
+ * "feature": "vitrina_ads",
152301
+ * "entitlement_state": "off"
152302
+ * }
152303
+ * }
152304
+ * }
152305
+ */
152306
+ "application/json": components["schemas"]["Error"];
152307
+ };
152308
+ };
152309
+ /** @description Forbidden */
152310
+ 403: {
152311
+ headers: {
152312
+ [name: string]: unknown;
152313
+ };
152314
+ content: {
152315
+ "application/json": components["schemas"]["Error"];
152316
+ };
152317
+ };
152318
+ /** @description Not found */
152319
+ 404: {
152320
+ headers: {
152321
+ [name: string]: unknown;
152322
+ };
152323
+ content: {
152324
+ "application/json": components["schemas"]["Error"];
152325
+ };
152326
+ };
152327
+ /** @description Vitrina Ads is active but the delegated key has not finished rotating (`ADS_KEY_NEEDS_REMINT`) — retry once `GET /ads/state` reports `needs_remint: false`. Also the generic conflict of a write. */
152328
+ 409: {
152329
+ headers: {
152330
+ [name: string]: unknown;
152331
+ };
152332
+ content: {
152333
+ /**
152334
+ * @example {
152335
+ * "error": {
152336
+ * "code": "ADS_KEY_NEEDS_REMINT",
152337
+ * "message": "Vitrina Ads is active but the delegated key has not finished rotating (key_kind: core)",
152338
+ * "details": {
152339
+ * "key_kind": "core"
152340
+ * }
152341
+ * }
152342
+ * }
152343
+ */
152344
+ "application/json": components["schemas"]["Error"];
152345
+ };
152346
+ };
152347
+ /** @description Rate limited */
152348
+ 429: {
152349
+ headers: {
152350
+ [name: string]: unknown;
152351
+ };
152352
+ content: {
152353
+ "application/json": components["schemas"]["Error"];
152354
+ };
152355
+ };
152356
+ };
152357
+ };
152358
+ put?: never;
152359
+ post?: never;
152360
+ delete?: never;
152361
+ options?: never;
152362
+ head?: never;
152363
+ patch?: never;
152364
+ trace?: never;
152365
+ };
152366
+ "/ads/audiences/customer-list": {
152367
+ parameters: {
152368
+ query?: never;
152369
+ header?: never;
152370
+ path?: never;
152371
+ cookie?: never;
152372
+ };
152373
+ /**
152374
+ * The paying-customers audience: state and consent
152375
+ * @description A Meta customer list named «Vitrina · Clientes que pagaron»: the people who paid in the last year, as hashed e-mail, phone, first name, last name and country (never RUT, amounts, treatments or clinical data; a paying guardian is sent as the guardian), marketing opt-outs and minors with a known birth date always excluded, refreshed daily by the measurement engine; usable in Meta for a 1 % lookalike (optional), exclusion or retargeting. Google Customer Match is never used for clinics. The read is also the capability check: `coming_soon` until the engine can build it.
152376
+ */
152377
+ get: {
152378
+ parameters: {
152379
+ query?: never;
152380
+ header?: never;
152381
+ path?: never;
152382
+ cookie?: never;
152383
+ };
152384
+ requestBody?: never;
152385
+ responses: {
152386
+ /** @description The audience state. */
152387
+ 200: {
152388
+ headers: {
152389
+ [name: string]: unknown;
152390
+ };
152391
+ content: {
152392
+ /**
152393
+ * @example {
152394
+ * "data": {
152395
+ * "state": "live",
152396
+ * "reason": null,
152397
+ * "names": {
152398
+ * "audience": "Vitrina · Clientes que pagaron",
152399
+ * "lookalike": "Vitrina · Parecidos a clientes que pagaron (1 %)"
152400
+ * },
152401
+ * "size": {
152402
+ * "lower": 1000,
152403
+ * "upper": 1200
152404
+ * },
152405
+ * "last_refreshed_at": "2026-10-03T06:40:00.000Z",
152406
+ * "lookalike": {
152407
+ * "state": "ready",
152408
+ * "ratio": 0.01
152409
+ * },
152410
+ * "optin": {
152411
+ * "accepted_at": "2026-10-01T21:10:00.000Z",
152412
+ * "consent_version": "2026-10-04.customer-list.v2",
152413
+ * "current": true,
152414
+ * "lookalike": true
152415
+ * },
152416
+ * "consent": {
152417
+ * "version": "2026-10-04.customer-list.v2",
152418
+ * "text": "Vitrina creará en tu cuenta de Meta una lista llamada «Vitrina · Clientes que pagaron»…",
152419
+ * "text_hash": "0000000000000000000000000000000000000000000000000000000000000000"
152420
+ * }
152421
+ * }
152422
+ * }
152423
+ */
152424
+ "application/json": {
152425
+ data: components["schemas"]["AdsAudienceStatus"];
151667
152426
  };
151668
152427
  };
151669
152428
  };
@@ -181563,7 +182322,7 @@ interface components {
181563
182322
  campaign_name: string | null;
181564
182323
  }[];
181565
182324
  };
181566
- /** @description The engine’s own vocabulary, e.g. `{ budget_change_pct: 20 }`. `url_tags_fix`: `{ ads: [{ external_id, expected_url_tags, url_tags }] }` — each ad’s current parameters (null = none) and exactly what it gets. «Enviar a Meta»: `send_setup`/`send_dataset` `{ dataset_id }`, `send_stage` `{ stage, enabled, meta_event, active_ad_sets }`. `campaign_pause`: `{ period_spend, period_days, delivering_ads_count, avg_daily_spend_7d }` — the campaign’s spend in the listed period (the account currency’s major units), the period’s days and its delivering ads, what the card draws at rest (spend → 0, ~spend/days a day); `avg_daily_spend_7d` is the live 7-day daily mean the preview shows (`AdsMoney`: exact decimal + currency), `null` when the list could not know it (real mode reads it only at preview) — fall back to `period_spend / period_days`, labelled as the period. `adset_pause` (D62): `{ ad_ids, period_spend, share, booking_rate, sibling_ad_set, sibling_booking_rate, budget_level }` — the ad set’s delivering ads (bound into the approval: only those the preview read as showing), its share of the campaign’s spend and its booking rate vs its best sibling’s; with a campaign budget Meta moves the money to the sibling. A `nav` card with `params.card = "campaign_budget"` is a scale on a campaign that owns its budget: `{ campaign_external_id, daily_budget, new_daily_budget, step_pct }` — the change is made in Meta (`href`). `params.card = "funnel_leak"` (D66): the account books under 10 % of the people who write from ads — `{ people, booked, booking_rate, booking_interval, benchmark, by_ad }`. Agenda capacity (clinics): a `nav` card with `params.card = "capacity_bottleneck"` REPLACES an ad's «Pausar» (`variant: "pause"`) or a «Subir» (`variant: "budget"`) when the specialty the ad books has no usable hour for more than 7 days (or under 2 bookable hours in 14): `{ card, variant, especialidad, mapping_source: "map" | "booked" | "name", unmapped: false, next_slot_day, searched_through, free_minutes_14, booked_minutes_14, fill_14, visit_minutes, evaluation_price, price_above_median, people, booked, booking_rate, median_booking_rate, waitlist_30d }` — counts, minutes and dates, never a probability; `waitlist_30d` = people waiting now for that specialty (the clinic waitlist demand feed), `null` when the feed is absent or 0 — the card then carries no «N personas esperan hora con X hoy» sentence. A `pause_ad` whose ad books an unknown specialty carries `params.capacity = { unmapped: true }` (ask which specialty; `tenant.settings.ads.specialty_map`). */
182325
+ /** @description The engine’s own vocabulary, e.g. `{ budget_change_pct: 20 }`. `url_tags_fix`: `{ ads: [{ external_id, expected_url_tags, url_tags }] }` — each ad’s current parameters (null = none) and exactly what it gets. «Enviar a Meta»: `send_setup`/`send_dataset` `{ dataset_id }`, `send_stage` `{ stage, enabled, meta_event, active_ad_sets }`. `campaign_pause`: `{ period_spend, period_days, delivering_ads_count, avg_daily_spend_7d }` — the campaign’s spend in the listed period (the account currency’s major units), the period’s days and its delivering ads, what the card draws at rest (spend → 0, ~spend/days a day); `avg_daily_spend_7d` is the live 7-day daily mean the preview shows (`AdsMoney`: exact decimal + currency), `null` when the list could not know it (real mode reads it only at preview) — fall back to `period_spend / period_days`, labelled as the period. `adset_pause` (D62): `{ ad_ids, period_spend, share, booking_rate, sibling_ad_set, sibling_booking_rate, budget_level }` — the ad set’s delivering ads (bound into the approval: only those the preview read as showing), its share of the campaign’s spend and its booking rate vs its best sibling’s; with a campaign budget Meta moves the money to the sibling. `budget_change` on a campaign (`target.level = "campaign"`, `engine_ref.op = "campaigns.budget"`, D78) raises or lowers a campaign that owns its budget (Advantage campaign budget) by `budget_change_pct` (±20 % at most, D68): `{ budget_change_pct, budget_level: "campaign", budget_type: "daily" | "lifetime", daily_budget, new_daily_budget (or lifetime_budget, new_lifetime_budget), step_pct }` — amounts in the account currency’s major units, `null` when the status read could not give them; the worker applies the step to the LIVE budget. A raise follows a winner; a lower is the D62 trim one level up (a campaign with more than its even share of the account’s spend booking under half the best other campaign’s rate). Undo = rollback. The optimisation-event cards (the duplicate card `params.optimization_event`, the experiment card, `adset_duplicate_swap`) follow D81: `appointment_booked` until the account reaches ~`first_payment_threshold` (50) first payments a week (`weekly_first_payments`), then `first_payment`. `params.card = "funnel_leak"` (D66): the account books under 10 % of the people who write from ads — `{ people, booked, booking_rate, booking_interval, benchmark, by_ad }`. Agenda capacity (clinics): a `nav` card with `params.card = "capacity_bottleneck"` REPLACES an ad's «Pausar» (`variant: "pause"`) or a «Subir» (`variant: "budget"`) when the specialty the ad books has no usable hour for more than 7 days (or under 2 bookable hours in 14): `{ card, variant, especialidad, mapping_source: "map" | "booked" | "name", unmapped: false, next_slot_day, searched_through, free_minutes_14, booked_minutes_14, fill_14, visit_minutes, evaluation_price, price_above_median, people, booked, booking_rate, median_booking_rate, waitlist_30d }` — counts, minutes and dates, never a probability; `waitlist_30d` = people waiting now for that specialty (the clinic waitlist demand feed), `null` when the feed is absent or 0 — the card then carries no «N personas esperan hora con X hoy» sentence. A `pause_ad` whose ad books an unknown specialty carries `params.capacity = { unmapped: true }` (ask which specialty; `tenant.settings.ads.specialty_map`). */
181567
182326
  params: {
181568
182327
  [key: string]: unknown;
181569
182328
  };
@@ -181573,7 +182332,7 @@ interface components {
181573
182332
  /** @description Which operation executes it; `null` for `nav`. */
181574
182333
  engine_ref: {
181575
182334
  /** @enum {string} */
181576
- op: "recommendations.apply" | "ads.pause" | "ads.pause_set" | "ads.resume" | "ads.resume_set" | "adsets.budget" | "campaigns.pause" | "campaigns.resume" | "wiring.ad_sets" | "quality.utm_refresh" | "wiring.recheck" | "actions.rollback" | "quality.url_tags_apply" | "quality.url_tags_undo" | "send.setup" | "send.dataset" | "send.stage" | "send.stop" | "send.restore";
182335
+ op: "recommendations.apply" | "ads.pause" | "ads.pause_set" | "ads.resume" | "ads.resume_set" | "adsets.budget" | "campaigns.budget" | "campaigns.pause" | "campaigns.resume" | "wiring.ad_sets" | "quality.utm_refresh" | "wiring.recheck" | "actions.rollback" | "quality.url_tags_apply" | "quality.url_tags_undo" | "send.setup" | "send.dataset" | "send.stage" | "send.stop" | "send.restore";
181577
182336
  recommendation_id?: string;
181578
182337
  } | null;
181579
182338
  /** @description `nav` only: a workspace-relative path (`/anuncios/atribuidos?c=…`, `/anuncios/activar?paso=tag`), or an absolute Meta Ads Manager URL built from Meta ids (`https://www.facebook.com/adsmanager/…`) — open it in a new tab; it changes nothing by itself. */
@@ -181664,7 +182423,7 @@ interface components {
181664
182423
  } | null;
181665
182424
  reversible: boolean;
181666
182425
  /** @enum {string|null} */
181667
- engine_op: "recommendations.apply" | "ads.pause" | "ads.pause_set" | "ads.resume" | "ads.resume_set" | "adsets.budget" | "campaigns.pause" | "campaigns.resume" | "wiring.ad_sets" | "quality.utm_refresh" | "wiring.recheck" | "actions.rollback" | "quality.url_tags_apply" | "quality.url_tags_undo" | "send.setup" | "send.dataset" | "send.stage" | "send.stop" | "send.restore" | null;
182426
+ engine_op: "recommendations.apply" | "ads.pause" | "ads.pause_set" | "ads.resume" | "ads.resume_set" | "adsets.budget" | "campaigns.budget" | "campaigns.pause" | "campaigns.resume" | "wiring.ad_sets" | "quality.utm_refresh" | "wiring.recheck" | "actions.rollback" | "quality.url_tags_apply" | "quality.url_tags_undo" | "send.setup" | "send.dataset" | "send.stage" | "send.stop" | "send.restore" | null;
181668
182427
  /** @description `campaign_pause` only: the line the sheet leads with — «Pausar «{campaign}» · detiene {n} anuncios · ~$X al día» (the count and the daily figure are dropped when unknown). */
181669
182428
  summary?: string;
181670
182429
  campaign?: components["schemas"]["AdsCampaignPauseSnapshot"];
@@ -182196,6 +182955,15 @@ interface components {
182196
182955
  * @enum {string|null}
182197
182956
  */
182198
182957
  reason: "not_sending" | "dataset_restricted" | "dataset_unknown" | "already_open" | null;
182958
+ /**
182959
+ * @description D81: the event the test arm should optimise for — `appointment_booked` («cita agendada») until the account reaches ~`first_payment_threshold` new patients’ first payments a week, then `first_payment`. The default `test_event` of `POST /ads/experiments`. Absent when the count could not be read.
182960
+ * @enum {string}
182961
+ */
182962
+ suggested_test_event?: "appointment_booked" | "first_payment";
182963
+ /** @description New patients’ first payments a week (one decimal; «Envío a Meta»’s «Por semana»), `null` = unknown. */
182964
+ weekly_first_payments?: number | null;
182965
+ /** @description First payments a week from which Meta optimises on the first payment (~50). */
182966
+ first_payment_threshold?: number;
182199
182967
  };
182200
182968
  AdsExperiment: {
182201
182969
  /** Format: uuid */