@vitrina/api 12.5.1 → 12.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.
Files changed (3) hide show
  1. package/dist/index.d.cts +2744 -455
  2. package/dist/index.d.ts +2744 -455
  3. package/package.json +1 -1
package/dist/index.d.cts CHANGED
@@ -6424,6 +6424,38 @@ interface paths {
6424
6424
  * "description": "Pipeline de ventas general: sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
6425
6425
  * "kind": "sales"
6426
6426
  * },
6427
+ * "funnel_steps": [
6428
+ * {
6429
+ * "key": "contact",
6430
+ * "name": "Contacto",
6431
+ * "outcome": "progress",
6432
+ * "position": 0
6433
+ * },
6434
+ * {
6435
+ * "key": "qualified",
6436
+ * "name": "Calificado",
6437
+ * "outcome": "progress",
6438
+ * "position": 1
6439
+ * },
6440
+ * {
6441
+ * "key": "proposal",
6442
+ * "name": "Propuesta",
6443
+ * "outcome": "progress",
6444
+ * "position": 2
6445
+ * },
6446
+ * {
6447
+ * "key": "won",
6448
+ * "name": "Ganado",
6449
+ * "outcome": "progress",
6450
+ * "position": 3
6451
+ * },
6452
+ * {
6453
+ * "key": "lost",
6454
+ * "name": "Perdido",
6455
+ * "outcome": "lost",
6456
+ * "position": 4
6457
+ * }
6458
+ * ],
6427
6459
  * "stages": [
6428
6460
  * {
6429
6461
  * "key": "new",
@@ -6438,7 +6470,8 @@ interface paths {
6438
6470
  * "won",
6439
6471
  * "lost",
6440
6472
  * "unqualified"
6441
- * ]
6473
+ * ],
6474
+ * "funnel_step": "contact"
6442
6475
  * },
6443
6476
  * {
6444
6477
  * "key": "contacted",
@@ -6454,7 +6487,8 @@ interface paths {
6454
6487
  * "won",
6455
6488
  * "lost",
6456
6489
  * "unqualified"
6457
- * ]
6490
+ * ],
6491
+ * "funnel_step": "contact"
6458
6492
  * }
6459
6493
  * ]
6460
6494
  * }
@@ -6628,6 +6662,7 @@ interface paths {
6628
6662
  * "is_human": false,
6629
6663
  * "is_marketing": false,
6630
6664
  * "template_stage_key": "new",
6665
+ * "funnel_step_key": "contact",
6631
6666
  * "created_at": "2026-09-04T19:07:05.226Z"
6632
6667
  * },
6633
6668
  * {
@@ -6652,6 +6687,7 @@ interface paths {
6652
6687
  * "is_human": false,
6653
6688
  * "is_marketing": false,
6654
6689
  * "template_stage_key": "contacted",
6690
+ * "funnel_step_key": "contact",
6655
6691
  * "created_at": "2026-09-04T19:07:05.226Z"
6656
6692
  * },
6657
6693
  * {
@@ -6672,6 +6708,7 @@ interface paths {
6672
6708
  * "is_human": false,
6673
6709
  * "is_marketing": false,
6674
6710
  * "template_stage_key": "won",
6711
+ * "funnel_step_key": "won",
6675
6712
  * "created_at": "2026-09-04T19:07:05.226Z"
6676
6713
  * }
6677
6714
  * ]
@@ -6748,7 +6785,7 @@ interface paths {
6748
6785
  patch?: never;
6749
6786
  trace?: never;
6750
6787
  };
6751
- "/pipelines/{id}": {
6788
+ "/pipelines/{id}/funnel-steps": {
6752
6789
  parameters: {
6753
6790
  query?: never;
6754
6791
  header?: never;
@@ -6756,8 +6793,8 @@ interface paths {
6756
6793
  cookie?: never;
6757
6794
  };
6758
6795
  /**
6759
- * Get pipeline
6760
- * @description One board WITH its columns nested under `stage[]`, in position order — the single call that renders a board. A stage's `ai_agent` embed is omitted for a caller without `ai_agents:read`.
6796
+ * Get pipeline funnel steps
6797
+ * @description The board's Pasos del embudo (ADR 0132): the few ordered steps its stages roll up into when the business reads its funnel, each with the stages it holds in board order — so a step holding several stages carries its per-stage split. The STEP LIST is declared by the board's template; a board with no template of its own (or one this release no longer ships) reads the generic template of its kind and says so with `inherited: true`. `outcome: lost` marks the step that holds lost and unqualified stages, reported apart from the path. `unplaced_stages` lists every stage with no step (or a step the template does not declare) — it is never dropped. Reassign a stage with `PUT /stages/{id}` `{ "funnel_step_key": "<key>" }` (`null` un-places it); `default_step_key` is what the template would give it.
6761
6798
  */
6762
6799
  get: {
6763
6800
  parameters: {
@@ -6770,7 +6807,7 @@ interface paths {
6770
6807
  };
6771
6808
  requestBody?: never;
6772
6809
  responses: {
6773
- /** @description Pipeline */
6810
+ /** @description Pipeline funnel steps */
6774
6811
  200: {
6775
6812
  headers: {
6776
6813
  [name: string]: unknown;
@@ -6779,89 +6816,132 @@ interface paths {
6779
6816
  /**
6780
6817
  * @example {
6781
6818
  * "data": {
6782
- * "id": "44444444-0000-4000-8000-000000000001",
6783
- * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
6784
- * "name": "Ventas",
6785
- * "description": "Sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
6786
- * "slug": "generic-sales",
6787
- * "kind": "sales",
6788
- * "is_fallback": true,
6789
- * "template_key": "generic_sales",
6790
- * "template_version": 1,
6791
- * "template_locale": "es",
6792
- * "created_at": "2026-09-04T19:07:05.226Z",
6793
- * "updated_at": "2026-09-04T19:07:05.226Z",
6794
- * "stage": [
6819
+ * "pipeline_id": "44444444-0000-4000-8000-000000000001",
6820
+ * "steps_template_key": "clinic_treatments",
6821
+ * "inherited": false,
6822
+ * "locale": "es",
6823
+ * "steps": [
6795
6824
  * {
6796
- * "id": "55555555-0000-4000-8000-000000000001",
6797
- * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
6798
- * "pipeline_id": "44444444-0000-4000-8000-000000000001",
6799
- * "name": "Nuevo",
6800
- * "description": "Existe una consulta concreta, pero todavía no hubo contacto sustantivo ni calificación.",
6801
- * "slug": "new",
6825
+ * "key": "no_appointment",
6826
+ * "name": "Sin cita",
6827
+ * "outcome": "progress",
6802
6828
  * "position": 0,
6803
- * "category": "open",
6804
- * "is_terminal": false,
6805
- * "won_state": null,
6806
- * "sla_days": null,
6807
- * "allowed_transitions_to": [
6808
- * "55555555-0000-4000-8000-000000000002",
6809
- * "55555555-0000-4000-8000-000000000006",
6810
- * "55555555-0000-4000-8000-000000000007"
6811
- * ],
6812
- * "ai_agent_id": null,
6813
- * "ai_agent_graph_id": null,
6814
- * "is_human": false,
6815
- * "is_marketing": false,
6816
- * "template_stage_key": "new",
6817
- * "created_at": "2026-09-04T19:07:05.226Z",
6818
- * "ai_agent": null
6829
+ * "stages": [
6830
+ * {
6831
+ * "id": "55555555-0000-4000-8000-000000000101",
6832
+ * "name": "Nuevo",
6833
+ * "slug": "new",
6834
+ * "category": "open",
6835
+ * "position": 0,
6836
+ * "default_step_key": "no_appointment"
6837
+ * }
6838
+ * ]
6819
6839
  * },
6820
6840
  * {
6821
- * "id": "55555555-0000-4000-8000-000000000002",
6822
- * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
6823
- * "pipeline_id": "44444444-0000-4000-8000-000000000001",
6824
- * "name": "Contactado",
6825
- * "description": "Alguien del equipo hizo contacto real y está entendiendo la necesidad.",
6826
- * "slug": "contacted",
6841
+ * "key": "scheduled",
6842
+ * "name": "Agendado",
6843
+ * "outcome": "progress",
6827
6844
  * "position": 1,
6828
- * "category": "open",
6829
- * "is_terminal": false,
6830
- * "won_state": null,
6831
- * "sla_days": 3,
6832
- * "allowed_transitions_to": [
6833
- * "55555555-0000-4000-8000-000000000003",
6834
- * "55555555-0000-4000-8000-000000000006",
6835
- * "55555555-0000-4000-8000-000000000007"
6836
- * ],
6837
- * "ai_agent_id": null,
6838
- * "ai_agent_graph_id": null,
6839
- * "is_human": false,
6840
- * "is_marketing": false,
6841
- * "template_stage_key": "contacted",
6842
- * "created_at": "2026-09-04T19:07:05.226Z",
6843
- * "ai_agent": null
6845
+ * "stages": [
6846
+ * {
6847
+ * "id": "55555555-0000-4000-8000-000000000102",
6848
+ * "name": "Evaluación agendada",
6849
+ * "slug": "evaluation-scheduled",
6850
+ * "category": "open",
6851
+ * "position": 1,
6852
+ * "default_step_key": "scheduled"
6853
+ * },
6854
+ * {
6855
+ * "id": "55555555-0000-4000-8000-000000000103",
6856
+ * "name": "No asistió",
6857
+ * "slug": "no-show",
6858
+ * "category": "open",
6859
+ * "position": 2,
6860
+ * "default_step_key": "scheduled"
6861
+ * }
6862
+ * ]
6844
6863
  * },
6845
6864
  * {
6846
- * "id": "55555555-0000-4000-8000-000000000006",
6847
- * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
6848
- * "pipeline_id": "44444444-0000-4000-8000-000000000001",
6849
- * "name": "Ganado",
6850
- * "description": "La oportunidad se cerró a favor.",
6851
- * "slug": "won",
6852
- * "position": 6,
6853
- * "category": "won",
6854
- * "is_terminal": true,
6855
- * "won_state": "won",
6856
- * "sla_days": null,
6857
- * "allowed_transitions_to": [],
6858
- * "ai_agent_id": null,
6859
- * "ai_agent_graph_id": null,
6860
- * "is_human": false,
6861
- * "is_marketing": false,
6862
- * "template_stage_key": "won",
6863
- * "created_at": "2026-09-04T19:07:05.226Z",
6864
- * "ai_agent": null
6865
+ * "key": "evaluated",
6866
+ * "name": "Evaluado",
6867
+ * "outcome": "progress",
6868
+ * "position": 2,
6869
+ * "stages": [
6870
+ * {
6871
+ * "id": "55555555-0000-4000-8000-000000000104",
6872
+ * "name": "Evaluado",
6873
+ * "slug": "evaluated",
6874
+ * "category": "open",
6875
+ * "position": 3,
6876
+ * "default_step_key": "evaluated"
6877
+ * }
6878
+ * ]
6879
+ * },
6880
+ * {
6881
+ * "key": "sale",
6882
+ * "name": "Venta",
6883
+ * "outcome": "progress",
6884
+ * "position": 3,
6885
+ * "stages": [
6886
+ * {
6887
+ * "id": "55555555-0000-4000-8000-000000000105",
6888
+ * "name": "Presupuesto enviado",
6889
+ * "slug": "budget-sent",
6890
+ * "category": "open",
6891
+ * "position": 4,
6892
+ * "default_step_key": "sale"
6893
+ * },
6894
+ * {
6895
+ * "id": "55555555-0000-4000-8000-000000000106",
6896
+ * "name": "Aceptado",
6897
+ * "slug": "budget-accepted",
6898
+ * "category": "open",
6899
+ * "position": 5,
6900
+ * "default_step_key": "sale"
6901
+ * },
6902
+ * {
6903
+ * "id": "55555555-0000-4000-8000-000000000107",
6904
+ * "name": "Tratamiento iniciado",
6905
+ * "slug": "won",
6906
+ * "category": "won",
6907
+ * "position": 6,
6908
+ * "default_step_key": "sale"
6909
+ * }
6910
+ * ]
6911
+ * },
6912
+ * {
6913
+ * "key": "lost",
6914
+ * "name": "Perdido",
6915
+ * "outcome": "lost",
6916
+ * "position": 4,
6917
+ * "stages": [
6918
+ * {
6919
+ * "id": "55555555-0000-4000-8000-000000000108",
6920
+ * "name": "Perdido",
6921
+ * "slug": "lost",
6922
+ * "category": "lost",
6923
+ * "position": 7,
6924
+ * "default_step_key": "lost"
6925
+ * },
6926
+ * {
6927
+ * "id": "55555555-0000-4000-8000-000000000109",
6928
+ * "name": "No calificado",
6929
+ * "slug": "unqualified",
6930
+ * "category": "unqualified",
6931
+ * "position": 8,
6932
+ * "default_step_key": "lost"
6933
+ * }
6934
+ * ]
6935
+ * }
6936
+ * ],
6937
+ * "unplaced_stages": [
6938
+ * {
6939
+ * "id": "55555555-0000-4000-8000-000000000110",
6940
+ * "name": "Control post tratamiento",
6941
+ * "slug": "control",
6942
+ * "category": "open",
6943
+ * "position": 9,
6944
+ * "default_step_key": null
6865
6945
  * }
6866
6946
  * ]
6867
6947
  * }
@@ -6931,11 +7011,26 @@ interface paths {
6931
7011
  };
6932
7012
  };
6933
7013
  };
7014
+ put?: never;
7015
+ post?: never;
7016
+ delete?: never;
7017
+ options?: never;
7018
+ head?: never;
7019
+ patch?: never;
7020
+ trace?: never;
7021
+ };
7022
+ "/pipelines/{id}": {
7023
+ parameters: {
7024
+ query?: never;
7025
+ header?: never;
7026
+ path?: never;
7027
+ cookie?: never;
7028
+ };
6934
7029
  /**
6935
- * Update pipeline
6936
- * @description Only the fields present in the body are written. `kind` is editable only for a non-fallback, EMPTY board that is not the last of its kind — otherwise 409. A workspace holds exactly one FALLBACK board per kind — the board a card lands on when nobody named one. `is_fallback: true` promotes this board and demotes the current holder of that kind; `false` is ignored (promote the other board instead, so there is never a moment with no fallback).
7030
+ * Get pipeline
7031
+ * @description One board WITH its columns nested under `stage[]`, in position order — the single call that renders a board. A stage's `ai_agent` embed is omitted for a caller without `ai_agents:read`.
6937
7032
  */
6938
- put: {
7033
+ get: {
6939
7034
  parameters: {
6940
7035
  query?: never;
6941
7036
  header?: never;
@@ -6944,23 +7039,7 @@ interface paths {
6944
7039
  };
6945
7040
  cookie?: never;
6946
7041
  };
6947
- requestBody?: {
6948
- content: {
6949
- /**
6950
- * @example {
6951
- * "description": "Oportunidades con empresas. Requiere aprobación de precio sobre 20 %."
6952
- * }
6953
- */
6954
- "application/json": {
6955
- name?: string;
6956
- description?: string;
6957
- slug?: string;
6958
- /** @enum {string} */
6959
- kind?: "ticket" | "sales" | "vehicle";
6960
- is_fallback?: boolean;
6961
- };
6962
- };
6963
- };
7042
+ requestBody?: never;
6964
7043
  responses: {
6965
7044
  /** @description Pipeline */
6966
7045
  200: {
@@ -6971,18 +7050,213 @@ interface paths {
6971
7050
  /**
6972
7051
  * @example {
6973
7052
  * "data": {
6974
- * "id": "44444444-0000-4000-8000-000000000002",
7053
+ * "id": "44444444-0000-4000-8000-000000000001",
6975
7054
  * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
6976
- * "name": "Ventas mayoristas",
6977
- * "description": "Oportunidades con empresas. Requiere aprobación de precio sobre 20 %.",
6978
- * "slug": "ventas-mayoristas",
7055
+ * "name": "Ventas",
7056
+ * "description": "Sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
7057
+ * "slug": "generic-sales",
6979
7058
  * "kind": "sales",
6980
- * "is_fallback": false,
6981
- * "template_key": null,
6982
- * "template_version": null,
6983
- * "template_locale": null,
6984
- * "created_at": "2026-09-22T08:41:22.273Z",
6985
- * "updated_at": "2026-09-22T08:41:22.273Z"
7059
+ * "is_fallback": true,
7060
+ * "template_key": "generic_sales",
7061
+ * "template_version": 1,
7062
+ * "template_locale": "es",
7063
+ * "created_at": "2026-09-04T19:07:05.226Z",
7064
+ * "updated_at": "2026-09-04T19:07:05.226Z",
7065
+ * "stage": [
7066
+ * {
7067
+ * "id": "55555555-0000-4000-8000-000000000001",
7068
+ * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
7069
+ * "pipeline_id": "44444444-0000-4000-8000-000000000001",
7070
+ * "name": "Nuevo",
7071
+ * "description": "Existe una consulta concreta, pero todavía no hubo contacto sustantivo ni calificación.",
7072
+ * "slug": "new",
7073
+ * "position": 0,
7074
+ * "category": "open",
7075
+ * "is_terminal": false,
7076
+ * "won_state": null,
7077
+ * "sla_days": null,
7078
+ * "allowed_transitions_to": [
7079
+ * "55555555-0000-4000-8000-000000000002",
7080
+ * "55555555-0000-4000-8000-000000000006",
7081
+ * "55555555-0000-4000-8000-000000000007"
7082
+ * ],
7083
+ * "ai_agent_id": null,
7084
+ * "ai_agent_graph_id": null,
7085
+ * "is_human": false,
7086
+ * "is_marketing": false,
7087
+ * "template_stage_key": "new",
7088
+ * "funnel_step_key": "contact",
7089
+ * "created_at": "2026-09-04T19:07:05.226Z",
7090
+ * "ai_agent": null
7091
+ * },
7092
+ * {
7093
+ * "id": "55555555-0000-4000-8000-000000000002",
7094
+ * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
7095
+ * "pipeline_id": "44444444-0000-4000-8000-000000000001",
7096
+ * "name": "Contactado",
7097
+ * "description": "Alguien del equipo hizo contacto real y está entendiendo la necesidad.",
7098
+ * "slug": "contacted",
7099
+ * "position": 1,
7100
+ * "category": "open",
7101
+ * "is_terminal": false,
7102
+ * "won_state": null,
7103
+ * "sla_days": 3,
7104
+ * "allowed_transitions_to": [
7105
+ * "55555555-0000-4000-8000-000000000003",
7106
+ * "55555555-0000-4000-8000-000000000006",
7107
+ * "55555555-0000-4000-8000-000000000007"
7108
+ * ],
7109
+ * "ai_agent_id": null,
7110
+ * "ai_agent_graph_id": null,
7111
+ * "is_human": false,
7112
+ * "is_marketing": false,
7113
+ * "template_stage_key": "contacted",
7114
+ * "funnel_step_key": "contact",
7115
+ * "created_at": "2026-09-04T19:07:05.226Z",
7116
+ * "ai_agent": null
7117
+ * },
7118
+ * {
7119
+ * "id": "55555555-0000-4000-8000-000000000006",
7120
+ * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
7121
+ * "pipeline_id": "44444444-0000-4000-8000-000000000001",
7122
+ * "name": "Ganado",
7123
+ * "description": "La oportunidad se cerró a favor.",
7124
+ * "slug": "won",
7125
+ * "position": 6,
7126
+ * "category": "won",
7127
+ * "is_terminal": true,
7128
+ * "won_state": "won",
7129
+ * "sla_days": null,
7130
+ * "allowed_transitions_to": [],
7131
+ * "ai_agent_id": null,
7132
+ * "ai_agent_graph_id": null,
7133
+ * "is_human": false,
7134
+ * "is_marketing": false,
7135
+ * "template_stage_key": "won",
7136
+ * "funnel_step_key": "won",
7137
+ * "created_at": "2026-09-04T19:07:05.226Z",
7138
+ * "ai_agent": null
7139
+ * }
7140
+ * ]
7141
+ * }
7142
+ * }
7143
+ */
7144
+ "application/json": {
7145
+ data?: unknown;
7146
+ meta?: {
7147
+ [key: string]: unknown;
7148
+ };
7149
+ };
7150
+ };
7151
+ };
7152
+ /** @description Validation error */
7153
+ 400: {
7154
+ headers: {
7155
+ [name: string]: unknown;
7156
+ };
7157
+ content: {
7158
+ "application/json": components["schemas"]["Error"];
7159
+ };
7160
+ };
7161
+ /** @description Unauthorized */
7162
+ 401: {
7163
+ headers: {
7164
+ [name: string]: unknown;
7165
+ };
7166
+ content: {
7167
+ "application/json": components["schemas"]["Error"];
7168
+ };
7169
+ };
7170
+ /** @description Forbidden */
7171
+ 403: {
7172
+ headers: {
7173
+ [name: string]: unknown;
7174
+ };
7175
+ content: {
7176
+ "application/json": components["schemas"]["Error"];
7177
+ };
7178
+ };
7179
+ /** @description Not found */
7180
+ 404: {
7181
+ headers: {
7182
+ [name: string]: unknown;
7183
+ };
7184
+ content: {
7185
+ "application/json": components["schemas"]["Error"];
7186
+ };
7187
+ };
7188
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
7189
+ 409: {
7190
+ headers: {
7191
+ [name: string]: unknown;
7192
+ };
7193
+ content: {
7194
+ "application/json": components["schemas"]["Error"];
7195
+ };
7196
+ };
7197
+ /** @description Rate limited */
7198
+ 429: {
7199
+ headers: {
7200
+ [name: string]: unknown;
7201
+ };
7202
+ content: {
7203
+ "application/json": components["schemas"]["Error"];
7204
+ };
7205
+ };
7206
+ };
7207
+ };
7208
+ /**
7209
+ * Update pipeline
7210
+ * @description Only the fields present in the body are written. `kind` is editable only for a non-fallback, EMPTY board that is not the last of its kind — otherwise 409. A workspace holds exactly one FALLBACK board per kind — the board a card lands on when nobody named one. `is_fallback: true` promotes this board and demotes the current holder of that kind; `false` is ignored (promote the other board instead, so there is never a moment with no fallback).
7211
+ */
7212
+ put: {
7213
+ parameters: {
7214
+ query?: never;
7215
+ header?: never;
7216
+ path: {
7217
+ id: string;
7218
+ };
7219
+ cookie?: never;
7220
+ };
7221
+ requestBody?: {
7222
+ content: {
7223
+ /**
7224
+ * @example {
7225
+ * "description": "Oportunidades con empresas. Requiere aprobación de precio sobre 20 %."
7226
+ * }
7227
+ */
7228
+ "application/json": {
7229
+ name?: string;
7230
+ description?: string;
7231
+ slug?: string;
7232
+ /** @enum {string} */
7233
+ kind?: "ticket" | "sales" | "vehicle";
7234
+ is_fallback?: boolean;
7235
+ };
7236
+ };
7237
+ };
7238
+ responses: {
7239
+ /** @description Pipeline */
7240
+ 200: {
7241
+ headers: {
7242
+ [name: string]: unknown;
7243
+ };
7244
+ content: {
7245
+ /**
7246
+ * @example {
7247
+ * "data": {
7248
+ * "id": "44444444-0000-4000-8000-000000000002",
7249
+ * "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
7250
+ * "name": "Ventas mayoristas",
7251
+ * "description": "Oportunidades con empresas. Requiere aprobación de precio sobre 20 %.",
7252
+ * "slug": "ventas-mayoristas",
7253
+ * "kind": "sales",
7254
+ * "is_fallback": false,
7255
+ * "template_key": null,
7256
+ * "template_version": null,
7257
+ * "template_locale": null,
7258
+ * "created_at": "2026-09-22T08:41:22.273Z",
7259
+ * "updated_at": "2026-09-22T08:41:22.273Z"
6986
7260
  * }
6987
7261
  * }
6988
7262
  */
@@ -7152,6 +7426,324 @@ interface paths {
7152
7426
  patch?: never;
7153
7427
  trace?: never;
7154
7428
  };
7429
+ "/funnel-report": {
7430
+ parameters: {
7431
+ query?: never;
7432
+ header?: never;
7433
+ path?: never;
7434
+ cookie?: never;
7435
+ };
7436
+ /**
7437
+ * Get the funnel report
7438
+ * @description The board's Pasos del embudo (ADR 0132) with the number of leads that reached each one in the period, the conversion from the step before, the split by stage inside each step, and Perdido apart. Counts only: this is the door the aggregates-only Consultor seat reads, next to `leads:read`. WHAT A COUNT MEANS. The cohort is the leads of the board CREATED in the period (`from` to `to`, both days inclusive, in the workspace clock). A lead counts in a step when it has ever stood on a stage of that step OR OF A LATER ONE — from its stage history, or from its current stage when it has none — so a lead that skipped a step still counts for the steps before it, and every cohort lead counts in the first step. The cohort is fixed by the creation date but how far it got is read as of now: a lead created in the period and sold next month counts in the last step the next time you look. `current` is where the cohort stands today. `lost` is the cohort leads standing on a lost stage today, split by the step they were lost from; `step_key: null` is history that cannot say (never guessed into a step). `sum(steps[].current) + lost.total + unplaced = cohort`.
7439
+ */
7440
+ get: {
7441
+ parameters: {
7442
+ query?: {
7443
+ /** @description The board to read. Defaults to the workspace's sales board. */
7444
+ pipeline_id?: string;
7445
+ /** @description First day of the period, inclusive. Defaults to 29 days before `to`. */
7446
+ from?: string;
7447
+ /** @description Last day of the period, INCLUSIVE. Defaults to today in the workspace's clock. */
7448
+ to?: string;
7449
+ };
7450
+ header?: never;
7451
+ path?: never;
7452
+ cookie?: never;
7453
+ };
7454
+ requestBody?: never;
7455
+ responses: {
7456
+ /** @description Funnel report */
7457
+ 200: {
7458
+ headers: {
7459
+ [name: string]: unknown;
7460
+ };
7461
+ content: {
7462
+ /**
7463
+ * @example {
7464
+ * "data": {
7465
+ * "pipeline_id": "0b1c2d3e-0000-4000-8000-000000000001",
7466
+ * "period": {
7467
+ * "from": "2026-09-10",
7468
+ * "to": "2026-10-09",
7469
+ * "timezone": "America/Santiago"
7470
+ * },
7471
+ * "cohort": 120,
7472
+ * "steps": [
7473
+ * {
7474
+ * "key": "no_appointment",
7475
+ * "name": "Sin cita",
7476
+ * "position": 0,
7477
+ * "reached": 120,
7478
+ * "current": 61,
7479
+ * "conversion_from_previous": null,
7480
+ * "conversion_from_start": 1,
7481
+ * "stages": [
7482
+ * {
7483
+ * "id": "0b1c2d3e-0000-4000-8000-0000000000a1",
7484
+ * "name": "Nuevo",
7485
+ * "reached": 120,
7486
+ * "current": 61
7487
+ * }
7488
+ * ]
7489
+ * },
7490
+ * {
7491
+ * "key": "scheduled",
7492
+ * "name": "Agendado",
7493
+ * "position": 1,
7494
+ * "reached": 48,
7495
+ * "current": 14,
7496
+ * "conversion_from_previous": 0.4,
7497
+ * "conversion_from_start": 0.4,
7498
+ * "stages": []
7499
+ * }
7500
+ * ],
7501
+ * "lost": {
7502
+ * "key": "lost",
7503
+ * "name": "Perdido",
7504
+ * "total": 22,
7505
+ * "from_steps": [
7506
+ * {
7507
+ * "step_key": "no_appointment",
7508
+ * "step_name": "Sin cita",
7509
+ * "count": 15
7510
+ * },
7511
+ * {
7512
+ * "step_key": "scheduled",
7513
+ * "step_name": "Agendado",
7514
+ * "count": 5
7515
+ * },
7516
+ * {
7517
+ * "step_key": null,
7518
+ * "step_name": null,
7519
+ * "count": 2
7520
+ * }
7521
+ * ],
7522
+ * "stages": []
7523
+ * },
7524
+ * "unplaced": 0,
7525
+ * "unplaced_stages": []
7526
+ * }
7527
+ * }
7528
+ */
7529
+ "application/json": {
7530
+ data?: unknown;
7531
+ meta?: {
7532
+ [key: string]: unknown;
7533
+ };
7534
+ };
7535
+ };
7536
+ };
7537
+ /** @description Validation error */
7538
+ 400: {
7539
+ headers: {
7540
+ [name: string]: unknown;
7541
+ };
7542
+ content: {
7543
+ "application/json": components["schemas"]["Error"];
7544
+ };
7545
+ };
7546
+ /** @description Unauthorized */
7547
+ 401: {
7548
+ headers: {
7549
+ [name: string]: unknown;
7550
+ };
7551
+ content: {
7552
+ "application/json": components["schemas"]["Error"];
7553
+ };
7554
+ };
7555
+ /** @description Forbidden */
7556
+ 403: {
7557
+ headers: {
7558
+ [name: string]: unknown;
7559
+ };
7560
+ content: {
7561
+ "application/json": components["schemas"]["Error"];
7562
+ };
7563
+ };
7564
+ /** @description Not found */
7565
+ 404: {
7566
+ headers: {
7567
+ [name: string]: unknown;
7568
+ };
7569
+ content: {
7570
+ "application/json": components["schemas"]["Error"];
7571
+ };
7572
+ };
7573
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
7574
+ 409: {
7575
+ headers: {
7576
+ [name: string]: unknown;
7577
+ };
7578
+ content: {
7579
+ "application/json": components["schemas"]["Error"];
7580
+ };
7581
+ };
7582
+ /** @description Rate limited */
7583
+ 429: {
7584
+ headers: {
7585
+ [name: string]: unknown;
7586
+ };
7587
+ content: {
7588
+ "application/json": components["schemas"]["Error"];
7589
+ };
7590
+ };
7591
+ };
7592
+ };
7593
+ put?: never;
7594
+ post?: never;
7595
+ delete?: never;
7596
+ options?: never;
7597
+ head?: never;
7598
+ patch?: never;
7599
+ trace?: never;
7600
+ };
7601
+ "/funnel-report/people": {
7602
+ parameters: {
7603
+ query?: never;
7604
+ header?: never;
7605
+ path?: never;
7606
+ cookie?: never;
7607
+ };
7608
+ /**
7609
+ * List the people behind a funnel count
7610
+ * @description The leads behind one number of `GET /funnel-report`, read through the same cohort and the same rules, so the list always has as many rows as the count says (`total`). `measure=reached&step=` is a step headline; `measure=current` is who stands there today; `measure=lost` is Perdido (narrow with `step`, or `lost_from_unknown=true`); `measure=unplaced` is the leads on a stage that belongs to no step. Add `stage_id` for one stage of a step. Newest activity first. Needs `leads:read`; the contact name rides only with `contacts:read`. WHAT A COUNT MEANS. The cohort is the leads of the board CREATED in the period (`from` to `to`, both days inclusive, in the workspace clock). A lead counts in a step when it has ever stood on a stage of that step OR OF A LATER ONE — from its stage history, or from its current stage when it has none — so a lead that skipped a step still counts for the steps before it, and every cohort lead counts in the first step. The cohort is fixed by the creation date but how far it got is read as of now: a lead created in the period and sold next month counts in the last step the next time you look. `current` is where the cohort stands today. `lost` is the cohort leads standing on a lost stage today, split by the step they were lost from; `step_key: null` is history that cannot say (never guessed into a step). `sum(steps[].current) + lost.total + unplaced = cohort`.
7611
+ */
7612
+ get: {
7613
+ parameters: {
7614
+ query: {
7615
+ /** @description The board to read. Defaults to the workspace's sales board. */
7616
+ pipeline_id?: string;
7617
+ /** @description First day of the period, inclusive. Defaults to 29 days before `to`. */
7618
+ from?: string;
7619
+ /** @description Last day of the period, INCLUSIVE. Defaults to today in the workspace's clock. */
7620
+ to?: string;
7621
+ /** @description `reached`: leads that reached `step` or a later one (the headline count). `current`: leads standing in `step` today. `lost`: leads standing on a lost stage today. `unplaced`: leads on a stage that belongs to no step. */
7622
+ measure: "reached" | "current" | "lost" | "unplaced";
7623
+ /** @description A step key from the report. Required for `reached` and `current`; for `lost` it narrows to the step the lead was lost from. */
7624
+ step?: string;
7625
+ /** @description Narrow to one stage of the step (the per-stage split). */
7626
+ stage_id?: string;
7627
+ /** @description `measure=lost` only: the leads whose history does not say which step they were lost from. */
7628
+ lost_from_unknown?: "true";
7629
+ limit?: number;
7630
+ offset?: number | null;
7631
+ };
7632
+ header?: never;
7633
+ path?: never;
7634
+ cookie?: never;
7635
+ };
7636
+ requestBody?: never;
7637
+ responses: {
7638
+ /** @description Funnel people */
7639
+ 200: {
7640
+ headers: {
7641
+ [name: string]: unknown;
7642
+ };
7643
+ content: {
7644
+ /**
7645
+ * @example {
7646
+ * "data": {
7647
+ * "pipeline_id": "0b1c2d3e-0000-4000-8000-000000000001",
7648
+ * "period": {
7649
+ * "from": "2026-09-10",
7650
+ * "to": "2026-10-09",
7651
+ * "timezone": "America/Santiago"
7652
+ * },
7653
+ * "total": 1,
7654
+ * "limit": 50,
7655
+ * "offset": 0,
7656
+ * "people": [
7657
+ * {
7658
+ * "lead_id": "0b1c2d3e-0000-4000-8000-0000000000f1",
7659
+ * "title": "Implante molar",
7660
+ * "status": "open",
7661
+ * "contact_id": "0b1c2d3e-0000-4000-8000-0000000000c1",
7662
+ * "contact_name": "Camila Rojas",
7663
+ * "stage_id": "0b1c2d3e-0000-4000-8000-0000000000a2",
7664
+ * "stage_name": "Evaluación agendada",
7665
+ * "created_at": "2026-09-12T14:03:00.000Z",
7666
+ * "last_activity_at": "2026-10-08T09:30:00.000Z",
7667
+ * "last_move_at": "2026-09-20T16:00:00.000Z",
7668
+ * "last_move_stage_name": "Evaluación agendada",
7669
+ * "reached_step_key": "scheduled"
7670
+ * }
7671
+ * ]
7672
+ * }
7673
+ * }
7674
+ */
7675
+ "application/json": {
7676
+ data?: unknown;
7677
+ meta?: {
7678
+ [key: string]: unknown;
7679
+ };
7680
+ };
7681
+ };
7682
+ };
7683
+ /** @description Validation error */
7684
+ 400: {
7685
+ headers: {
7686
+ [name: string]: unknown;
7687
+ };
7688
+ content: {
7689
+ "application/json": components["schemas"]["Error"];
7690
+ };
7691
+ };
7692
+ /** @description Unauthorized */
7693
+ 401: {
7694
+ headers: {
7695
+ [name: string]: unknown;
7696
+ };
7697
+ content: {
7698
+ "application/json": components["schemas"]["Error"];
7699
+ };
7700
+ };
7701
+ /** @description Forbidden */
7702
+ 403: {
7703
+ headers: {
7704
+ [name: string]: unknown;
7705
+ };
7706
+ content: {
7707
+ "application/json": components["schemas"]["Error"];
7708
+ };
7709
+ };
7710
+ /** @description Not found */
7711
+ 404: {
7712
+ headers: {
7713
+ [name: string]: unknown;
7714
+ };
7715
+ content: {
7716
+ "application/json": components["schemas"]["Error"];
7717
+ };
7718
+ };
7719
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
7720
+ 409: {
7721
+ headers: {
7722
+ [name: string]: unknown;
7723
+ };
7724
+ content: {
7725
+ "application/json": components["schemas"]["Error"];
7726
+ };
7727
+ };
7728
+ /** @description Rate limited */
7729
+ 429: {
7730
+ headers: {
7731
+ [name: string]: unknown;
7732
+ };
7733
+ content: {
7734
+ "application/json": components["schemas"]["Error"];
7735
+ };
7736
+ };
7737
+ };
7738
+ };
7739
+ put?: never;
7740
+ post?: never;
7741
+ delete?: never;
7742
+ options?: never;
7743
+ head?: never;
7744
+ patch?: never;
7745
+ trace?: never;
7746
+ };
7155
7747
  "/stages": {
7156
7748
  parameters: {
7157
7749
  query?: never;
@@ -7203,6 +7795,7 @@ interface paths {
7203
7795
  * "is_human": false,
7204
7796
  * "is_marketing": false,
7205
7797
  * "template_stage_key": "new",
7798
+ * "funnel_step_key": "contact",
7206
7799
  * "created_at": "2026-09-04T19:07:05.226Z"
7207
7800
  * },
7208
7801
  * {
@@ -7227,6 +7820,7 @@ interface paths {
7227
7820
  * "is_human": false,
7228
7821
  * "is_marketing": false,
7229
7822
  * "template_stage_key": "contacted",
7823
+ * "funnel_step_key": "contact",
7230
7824
  * "created_at": "2026-09-04T19:07:05.226Z"
7231
7825
  * },
7232
7826
  * {
@@ -7247,6 +7841,7 @@ interface paths {
7247
7841
  * "is_human": false,
7248
7842
  * "is_marketing": false,
7249
7843
  * "template_stage_key": "won",
7844
+ * "funnel_step_key": "won",
7250
7845
  * "created_at": "2026-09-04T19:07:05.226Z"
7251
7846
  * }
7252
7847
  * ],
@@ -7321,7 +7916,7 @@ interface paths {
7321
7916
  };
7322
7917
  /**
7323
7918
  * Update stages in bulk
7324
- * @description Reordering a board: an array of `{ id, …fields }`, up to 100. This is the endpoint a drag-and-drop save uses, because positions have to move together. Only the fields present on an element are written; the body is strict, so an unknown or derived key is a 400. `is_terminal` and `won_state` are DERIVED from `category` and are rejected as input (both bodies are strict, so sending either is a 400 naming the key). `sla_days` is the per-stage ageing threshold in days: an integer of at least 1, or `null` for "no limit" — omitting the key leaves the current value alone, sending `null` clears it, and those are different requests.
7919
+ * @description Reordering a board: an array of `{ id, …fields }`, up to 100. This is the endpoint a drag-and-drop save uses, because positions have to move together. Only the fields present on an element are written; the body is strict, so an unknown or derived key is a 400. `is_terminal` and `won_state` are DERIVED from `category` and are rejected as input (both bodies are strict, so sending either is a 400 naming the key). `sla_days` is the per-stage ageing threshold in days: an integer of at least 1, or `null` for "no limit" — omitting the key leaves the current value alone, sending `null` clears it, and those are different requests. `funnel_step_key` places the stage in one of its board's Pasos del embudo (`GET /pipelines/{id}/funnel-steps` lists them); a lost or unqualified stage goes to a `lost` step, every other stage to a `progress` step, else 400 naming the legal keys. On create, omitting it takes the template's default for the category; on update, omitting it leaves it alone, `null` un-places the stage, and a category change that puts the current step on the wrong side re-defaults it.
7325
7920
  */
7326
7921
  put: {
7327
7922
  parameters: {
@@ -7358,6 +7953,7 @@ interface paths {
7358
7953
  category?: "open" | "won" | "lost" | "unqualified" | "resolved";
7359
7954
  sla_days?: number | null;
7360
7955
  allowed_transitions_to?: string[];
7956
+ funnel_step_key?: string | null;
7361
7957
  }[];
7362
7958
  };
7363
7959
  };
@@ -7393,6 +7989,7 @@ interface paths {
7393
7989
  * "is_human": false,
7394
7990
  * "is_marketing": false,
7395
7991
  * "template_stage_key": "new",
7992
+ * "funnel_step_key": "contact",
7396
7993
  * "created_at": "2026-09-04T19:07:05.226Z"
7397
7994
  * },
7398
7995
  * {
@@ -7417,6 +8014,7 @@ interface paths {
7417
8014
  * "is_human": false,
7418
8015
  * "is_marketing": false,
7419
8016
  * "template_stage_key": "contacted",
8017
+ * "funnel_step_key": "contact",
7420
8018
  * "created_at": "2026-09-04T19:07:05.226Z"
7421
8019
  * }
7422
8020
  * ],
@@ -7491,7 +8089,7 @@ interface paths {
7491
8089
  };
7492
8090
  /**
7493
8091
  * Create stage
7494
- * @description Adds one column to a board. `slug` is generated from the name when omitted; `category` defaults to `open`. `is_terminal` and `won_state` are DERIVED from `category` and are rejected as input (both bodies are strict, so sending either is a 400 naming the key). `sla_days` is the per-stage ageing threshold in days: an integer of at least 1, or `null` for "no limit" — omitting the key leaves the current value alone, sending `null` clears it, and those are different requests.
8092
+ * @description Adds one column to a board. `slug` is generated from the name when omitted; `category` defaults to `open`. `is_terminal` and `won_state` are DERIVED from `category` and are rejected as input (both bodies are strict, so sending either is a 400 naming the key). `sla_days` is the per-stage ageing threshold in days: an integer of at least 1, or `null` for "no limit" — omitting the key leaves the current value alone, sending `null` clears it, and those are different requests. `funnel_step_key` places the stage in one of its board's Pasos del embudo (`GET /pipelines/{id}/funnel-steps` lists them); a lost or unqualified stage goes to a `lost` step, every other stage to a `progress` step, else 400 naming the legal keys. On create, omitting it takes the template's default for the category; on update, omitting it leaves it alone, `null` un-places the stage, and a category change that puts the current step on the wrong side re-defaults it.
7495
8093
  */
7496
8094
  post: {
7497
8095
  parameters: {
@@ -7538,6 +8136,7 @@ interface paths {
7538
8136
  category?: "open" | "won" | "lost" | "unqualified" | "resolved";
7539
8137
  sla_days?: number | null;
7540
8138
  allowed_transitions_to?: string[];
8139
+ funnel_step_key?: string | null;
7541
8140
  };
7542
8141
  };
7543
8142
  };
@@ -7572,6 +8171,7 @@ interface paths {
7572
8171
  * "is_human": false,
7573
8172
  * "is_marketing": false,
7574
8173
  * "template_stage_key": "contacted",
8174
+ * "funnel_step_key": "contact",
7575
8175
  * "created_at": "2026-09-04T19:07:05.226Z"
7576
8176
  * }
7577
8177
  * }
@@ -7701,6 +8301,7 @@ interface paths {
7701
8301
  category?: "open" | "won" | "lost" | "unqualified" | "resolved";
7702
8302
  sla_days?: number | null;
7703
8303
  allowed_transitions_to?: string[];
8304
+ funnel_step_key?: string | null;
7704
8305
  }[];
7705
8306
  };
7706
8307
  };
@@ -7736,6 +8337,7 @@ interface paths {
7736
8337
  * "is_human": false,
7737
8338
  * "is_marketing": false,
7738
8339
  * "template_stage_key": "new",
8340
+ * "funnel_step_key": "contact",
7739
8341
  * "created_at": "2026-09-04T19:07:05.226Z"
7740
8342
  * },
7741
8343
  * {
@@ -7756,6 +8358,7 @@ interface paths {
7756
8358
  * "is_human": false,
7757
8359
  * "is_marketing": false,
7758
8360
  * "template_stage_key": "won",
8361
+ * "funnel_step_key": "won",
7759
8362
  * "created_at": "2026-09-04T19:07:05.226Z"
7760
8363
  * }
7761
8364
  * ],
@@ -7883,6 +8486,7 @@ interface paths {
7883
8486
  * "is_human": false,
7884
8487
  * "is_marketing": false,
7885
8488
  * "template_stage_key": "new",
8489
+ * "funnel_step_key": "contact",
7886
8490
  * "created_at": "2026-09-04T19:07:05.226Z"
7887
8491
  * }
7888
8492
  * }
@@ -7953,7 +8557,7 @@ interface paths {
7953
8557
  };
7954
8558
  /**
7955
8559
  * Update stage
7956
- * @description Only the fields present in the body are written. `is_terminal` and `won_state` are DERIVED from `category` and are rejected as input (both bodies are strict, so sending either is a 400 naming the key). `sla_days` is the per-stage ageing threshold in days: an integer of at least 1, or `null` for "no limit" — omitting the key leaves the current value alone, sending `null` clears it, and those are different requests.
8560
+ * @description Only the fields present in the body are written. `is_terminal` and `won_state` are DERIVED from `category` and are rejected as input (both bodies are strict, so sending either is a 400 naming the key). `sla_days` is the per-stage ageing threshold in days: an integer of at least 1, or `null` for "no limit" — omitting the key leaves the current value alone, sending `null` clears it, and those are different requests. `funnel_step_key` places the stage in one of its board's Pasos del embudo (`GET /pipelines/{id}/funnel-steps` lists them); a lost or unqualified stage goes to a `lost` step, every other stage to a `progress` step, else 400 naming the legal keys. On create, omitting it takes the template's default for the category; on update, omitting it leaves it alone, `null` un-places the stage, and a category change that puts the current step on the wrong side re-defaults it.
7957
8561
  */
7958
8562
  put: {
7959
8563
  parameters: {
@@ -7969,7 +8573,8 @@ interface paths {
7969
8573
  /**
7970
8574
  * @example {
7971
8575
  * "position": 2,
7972
- * "sla_days": 5
8576
+ * "sla_days": 5,
8577
+ * "funnel_step_key": "qualified"
7973
8578
  * }
7974
8579
  */
7975
8580
  "application/json": {
@@ -7983,6 +8588,7 @@ interface paths {
7983
8588
  category?: "open" | "won" | "lost" | "unqualified" | "resolved";
7984
8589
  sla_days?: number | null;
7985
8590
  allowed_transitions_to?: string[];
8591
+ funnel_step_key?: string | null;
7986
8592
  };
7987
8593
  };
7988
8594
  };
@@ -8017,6 +8623,7 @@ interface paths {
8017
8623
  * "is_human": false,
8018
8624
  * "is_marketing": false,
8019
8625
  * "template_stage_key": "contacted",
8626
+ * "funnel_step_key": "contact",
8020
8627
  * "created_at": "2026-09-04T19:07:05.226Z"
8021
8628
  * }
8022
8629
  * }
@@ -21383,6 +21990,7 @@ interface paths {
21383
21990
  owner_user_id?: string;
21384
21991
  team_id?: string;
21385
21992
  contact_id?: string;
21993
+ linea_id?: string;
21386
21994
  min_value?: number | null;
21387
21995
  max_value?: number | null;
21388
21996
  min_score?: number | null;
@@ -22512,6 +23120,7 @@ interface paths {
22512
23120
  parameters: {
22513
23121
  query: {
22514
23122
  pipeline_id: string;
23123
+ linea_id?: string;
22515
23124
  card_kind?: "all" | "leads" | "solicitudes";
22516
23125
  first_touch?: "failed";
22517
23126
  first_touch_from?: string;
@@ -22648,6 +23257,7 @@ interface paths {
22648
23257
  parameters: {
22649
23258
  query: {
22650
23259
  pipeline_id: string;
23260
+ linea_id?: string;
22651
23261
  card_kind?: "all" | "leads" | "solicitudes";
22652
23262
  first_touch?: "failed";
22653
23263
  first_touch_from?: string;
@@ -24849,6 +25459,130 @@ interface paths {
24849
25459
  patch?: never;
24850
25460
  trace?: never;
24851
25461
  };
25462
+ "/leads/{id}/linea": {
25463
+ parameters: {
25464
+ query?: never;
25465
+ header?: never;
25466
+ path?: never;
25467
+ cookie?: never;
25468
+ };
25469
+ get?: never;
25470
+ /**
25471
+ * Set a lead's Línea (a person's choice)
25472
+ * @description Every lead of a clinic carries exactly one Línea (`linea` on the lead: `id`, `nombre`, `source`). It starts from the ad that opened the conversation (`source: ad`), is refined by the booked primera visita's prestación (`prestacion`), is General otherwise (`default`), and staff can correct it here (`staff`). A person's choice is final: nothing automatic changes it afterwards. The Línea must be one of the clinic's live Líneas (`GET /clinic/lineas`). Requires `leads:write`.
25473
+ */
25474
+ put: {
25475
+ parameters: {
25476
+ query?: never;
25477
+ header?: never;
25478
+ path: {
25479
+ id: string;
25480
+ };
25481
+ cookie?: never;
25482
+ };
25483
+ requestBody?: {
25484
+ content: {
25485
+ /**
25486
+ * @example {
25487
+ * "linea_id": "5b1f0c1e-0000-4000-8000-000000000001"
25488
+ * }
25489
+ */
25490
+ "application/json": {
25491
+ /** Format: uuid */
25492
+ linea_id: string;
25493
+ };
25494
+ };
25495
+ };
25496
+ responses: {
25497
+ /** @description The lead id and its Línea */
25498
+ 200: {
25499
+ headers: {
25500
+ [name: string]: unknown;
25501
+ };
25502
+ content: {
25503
+ /**
25504
+ * @example {
25505
+ * "data": {
25506
+ * "lead_id": "7c2d9a10-0000-4000-8000-000000000011",
25507
+ * "linea": {
25508
+ * "id": "5b1f0c1e-0000-4000-8000-000000000001",
25509
+ * "nombre": "Implantes",
25510
+ * "source": "staff"
25511
+ * }
25512
+ * }
25513
+ * }
25514
+ */
25515
+ "application/json": {
25516
+ data?: unknown;
25517
+ meta?: {
25518
+ [key: string]: unknown;
25519
+ };
25520
+ };
25521
+ };
25522
+ };
25523
+ /** @description Validation error */
25524
+ 400: {
25525
+ headers: {
25526
+ [name: string]: unknown;
25527
+ };
25528
+ content: {
25529
+ "application/json": components["schemas"]["Error"];
25530
+ };
25531
+ };
25532
+ /** @description Unauthorized */
25533
+ 401: {
25534
+ headers: {
25535
+ [name: string]: unknown;
25536
+ };
25537
+ content: {
25538
+ "application/json": components["schemas"]["Error"];
25539
+ };
25540
+ };
25541
+ /** @description Forbidden */
25542
+ 403: {
25543
+ headers: {
25544
+ [name: string]: unknown;
25545
+ };
25546
+ content: {
25547
+ "application/json": components["schemas"]["Error"];
25548
+ };
25549
+ };
25550
+ /** @description Not found */
25551
+ 404: {
25552
+ headers: {
25553
+ [name: string]: unknown;
25554
+ };
25555
+ content: {
25556
+ "application/json": components["schemas"]["Error"];
25557
+ };
25558
+ };
25559
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
25560
+ 409: {
25561
+ headers: {
25562
+ [name: string]: unknown;
25563
+ };
25564
+ content: {
25565
+ "application/json": components["schemas"]["Error"];
25566
+ };
25567
+ };
25568
+ /** @description Rate limited */
25569
+ 429: {
25570
+ headers: {
25571
+ [name: string]: unknown;
25572
+ };
25573
+ content: {
25574
+ "application/json": components["schemas"]["Error"];
25575
+ };
25576
+ };
25577
+ };
25578
+ };
25579
+ post?: never;
25580
+ delete?: never;
25581
+ options?: never;
25582
+ head?: never;
25583
+ patch?: never;
25584
+ trace?: never;
25585
+ };
24852
25586
  "/leads/{id}/won": {
24853
25587
  parameters: {
24854
25588
  query?: never;
@@ -50135,7 +50869,7 @@ interface paths {
50135
50869
  patch?: never;
50136
50870
  trace?: never;
50137
50871
  };
50138
- "/clinic/evaluation": {
50872
+ "/clinic/lineas": {
50139
50873
  parameters: {
50140
50874
  query?: never;
50141
50875
  header?: never;
@@ -50143,8 +50877,8 @@ interface paths {
50143
50877
  cookie?: never;
50144
50878
  };
50145
50879
  /**
50146
- * The clinic's «Evaluación»
50147
- * @description Clinic data the per-agent price policy reads: whether the clinic charges the evaluation (`declared: false` while it never said), a single amount (`null` = each specialty's evaluation prestación price, summarised in `default_amount`), whether it is waived when the patient starts treatment, and the convenios with a free evaluation. It reaches an agent's prompt only when that agent's `behavior_policies.treatment_price` is set. Requires `clinic:read`.
50880
+ * List the clinic's Líneas, with the mapping and the unmapped
50881
+ * @description A Línea is one entry in the clinic's own short list of what patients come for (Implantes, Estética, Endodoncia, TTM…), usually fewer than its specialties. The list is seeded from the roster's specialties on first read and is editable. Every roster specialty and every ad maps into exactly one Línea: `specialties` and `rules` (name fragments an ad's campaign, ad set or ad name must contain) say how; `unmapped_specialties` and the `ads` with `linea_id: null` are the ones the owner still has to place. Each lead carries exactly one Línea (`linea` on the lead). Requires `clinic:read`.
50148
50882
  *
50149
50883
  * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
50150
50884
  */
@@ -50157,7 +50891,7 @@ interface paths {
50157
50891
  };
50158
50892
  requestBody?: never;
50159
50893
  responses: {
50160
- /** @description The evaluation settings */
50894
+ /** @description Líneas */
50161
50895
  200: {
50162
50896
  headers: {
50163
50897
  [name: string]: unknown;
@@ -50166,77 +50900,53 @@ interface paths {
50166
50900
  /**
50167
50901
  * @example {
50168
50902
  * "data": {
50169
- * "declared": true,
50170
- * "charged": true,
50171
- * "amount_clp": null,
50172
- * "waived_on_treatment_start": true,
50173
- * "free_agreements": [
50903
+ * "lineas": [
50174
50904
  * {
50175
- * "id": "7c1e2f4a-0000-4000-8000-000000000001",
50176
- * "name": "Convenio Club Deportivo"
50905
+ * "id": "5b1f0c1e-0000-4000-8000-000000000001",
50906
+ * "nombre": "Implantes",
50907
+ * "is_default": false,
50908
+ * "archived": false,
50909
+ * "leads": 41,
50910
+ * "open_leads": 33,
50911
+ * "rules": [
50912
+ * {
50913
+ * "id": "5b1f0c1e-0000-4000-8000-000000000003",
50914
+ * "match": "implant",
50915
+ * "source": "seed"
50916
+ * }
50917
+ * ],
50918
+ * "specialties": [
50919
+ * {
50920
+ * "id": "5b1f0c1e-0000-4000-8000-000000000002",
50921
+ * "nombre": "Implantología"
50922
+ * }
50923
+ * ]
50177
50924
  * }
50178
50925
  * ],
50179
- * "default_amount": {
50180
- * "min_clp": 15000,
50181
- * "max_clp": 50000,
50182
- * "services": [
50183
- * {
50184
- * "id": "5d2b9e10-0000-4000-8000-000000000001",
50185
- * "nombre": "Evaluación medicina estética facial",
50186
- * "categoria": null,
50187
- * "precio_clp": 15000
50188
- * },
50189
- * {
50190
- * "id": "5d2b9e10-0000-4000-8000-000000000002",
50191
- * "nombre": "Evaluación y diagnóstico dental inicial",
50192
- * "categoria": null,
50193
- * "precio_clp": 50000
50194
- * }
50195
- * ],
50196
- * "service_count": 2
50197
- * },
50198
- * "agreements": [
50926
+ * "unmapped_specialties": [
50199
50927
  * {
50200
- * "id": "7c1e2f4a-0000-4000-8000-000000000001",
50201
- * "name": "Convenio Club Deportivo"
50928
+ * "id": "5b1f0c1e-0000-4000-8000-000000000004",
50929
+ * "nombre": "Prostodoncia"
50930
+ * }
50931
+ * ],
50932
+ * "ads": [
50933
+ * {
50934
+ * "external_id": "120250735360390233",
50935
+ * "campaign_name": "Campaña de mensajes IMPLANTES",
50936
+ * "ad_set_name": "INTERESES",
50937
+ * "ad_name": "Implantes 2",
50938
+ * "linea_id": "5b1f0c1e-0000-4000-8000-000000000001",
50939
+ * "via": "rule",
50940
+ * "matched": "implant",
50941
+ * "conversations": 119,
50942
+ * "leads": 38
50202
50943
  * }
50203
50944
  * ]
50204
50945
  * }
50205
50946
  * }
50206
50947
  */
50207
50948
  "application/json": {
50208
- data: {
50209
- /** @description False while the clinic never said whether it charges the evaluation. */
50210
- declared: boolean;
50211
- charged: boolean | null;
50212
- amount_clp: number | null;
50213
- waived_on_treatment_start: boolean;
50214
- /** @description The convenios with a free evaluation that still exist. */
50215
- free_agreements: {
50216
- /** Format: uuid */
50217
- id: string;
50218
- name: string;
50219
- }[];
50220
- /** @description What `amount_clp: null` means for this clinic: the priced evaluation prestaciones (`is_entry`) in the aranceles the agent quotes, cheapest first (at most 20 listed). Null when none is priced. */
50221
- default_amount: {
50222
- min_clp: number;
50223
- max_clp: number;
50224
- services: {
50225
- /** Format: uuid */
50226
- id: string;
50227
- nombre: string;
50228
- categoria: string | null;
50229
- precio_clp: number;
50230
- }[];
50231
- service_count: number;
50232
- } | null;
50233
- /** @description The clinic's active convenios — the options for `free_agreement_ids`. */
50234
- agreements: {
50235
- /** Format: uuid */
50236
- id: string;
50237
- name: string;
50238
- }[];
50239
- };
50949
+ data: components["schemas"]["ClinicLineasOverview"];
50240
50950
  };
50241
50951
  };
50242
50952
  };
@@ -50296,16 +51006,20 @@ interface paths {
50296
51006
  };
50297
51007
  };
50298
51008
  };
51009
+ put?: never;
50299
51010
  /**
50300
- * Update the clinic's «Evaluación»
50301
- * @description Partial: merges field by field over what is stored. `free_agreement_ids` must be convenios of this workspace (400 otherwise). Returns the same view as the GET. Audited. Requires `clinic_admin:write`.
51011
+ * Add a Línea
51012
+ * @description Find-or-create by name (accent- and case-insensitive). Requires `clinic_admin:write`.
50302
51013
  *
50303
51014
  * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
50304
51015
  */
50305
- put: {
51016
+ post: {
50306
51017
  parameters: {
50307
51018
  query?: never;
50308
- header?: never;
51019
+ header?: {
51020
+ /** @description Replay-safe retries: resending the SAME key with the SAME body returns the original response (`X-Idempotent-Replay: 1`) instead of creating a second copy — safe to send whenever a response might not have arrived. The same key with a DIFFERENT body answers `409 IDEMPOTENCY_KEY_CONFLICT`; use a fresh key per operation. */
51021
+ "Idempotency-Key"?: string;
51022
+ };
50309
51023
  path?: never;
50310
51024
  cookie?: never;
50311
51025
  };
@@ -50313,25 +51027,17 @@ interface paths {
50313
51027
  content: {
50314
51028
  /**
50315
51029
  * @example {
50316
- * "charged": true,
50317
- * "waived_on_treatment_start": true
51030
+ * "nombre": "Implantes"
50318
51031
  * }
50319
51032
  */
50320
51033
  "application/json": {
50321
- /** @description Whether the clinic charges the evaluation. */
50322
- charged?: boolean;
50323
- /** @description One amount in whole CLP for every evaluation. Null = each specialty's evaluation prestación (`is_entry`) price from the catalogue. */
50324
- amount_clp?: number | null;
50325
- /** @description «No se cobra si inicia tratamiento»: the evaluation is credited to the treatment when the patient starts it. */
50326
- waived_on_treatment_start?: boolean;
50327
- /** @description Convenios (`clinic_agreement` ids) whose members get the evaluation free. */
50328
- free_agreement_ids?: string[];
51034
+ nombre: string;
50329
51035
  };
50330
51036
  };
50331
51037
  };
50332
51038
  responses: {
50333
- /** @description The updated evaluation settings */
50334
- 200: {
51039
+ /** @description Línea */
51040
+ 201: {
50335
51041
  headers: {
50336
51042
  [name: string]: unknown;
50337
51043
  };
@@ -50339,77 +51045,968 @@ interface paths {
50339
51045
  /**
50340
51046
  * @example {
50341
51047
  * "data": {
50342
- * "declared": true,
50343
- * "charged": true,
50344
- * "amount_clp": null,
50345
- * "waived_on_treatment_start": true,
50346
- * "free_agreements": [
50347
- * {
50348
- * "id": "7c1e2f4a-0000-4000-8000-000000000001",
50349
- * "name": "Convenio Club Deportivo"
50350
- * }
50351
- * ],
50352
- * "default_amount": {
50353
- * "min_clp": 15000,
50354
- * "max_clp": 50000,
50355
- * "services": [
50356
- * {
50357
- * "id": "5d2b9e10-0000-4000-8000-000000000001",
50358
- * "nombre": "Evaluación medicina estética facial",
50359
- * "categoria": null,
50360
- * "precio_clp": 15000
50361
- * },
50362
- * {
50363
- * "id": "5d2b9e10-0000-4000-8000-000000000002",
50364
- * "nombre": "Evaluación y diagnóstico dental inicial",
50365
- * "categoria": null,
50366
- * "precio_clp": 50000
50367
- * }
50368
- * ],
50369
- * "service_count": 2
50370
- * },
50371
- * "agreements": [
50372
- * {
50373
- * "id": "7c1e2f4a-0000-4000-8000-000000000001",
50374
- * "name": "Convenio Club Deportivo"
50375
- * }
50376
- * ]
51048
+ * "id": "5b1f0c1e-0000-4000-8000-000000000001",
51049
+ * "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
51050
+ * "nombre": "Implantes",
51051
+ * "is_default": false,
51052
+ * "archived_at": null,
51053
+ * "created_at": "2026-10-09T12:00:00.000Z",
51054
+ * "updated_at": "2026-10-09T12:00:00.000Z"
50377
51055
  * }
50378
51056
  * }
50379
51057
  */
50380
51058
  "application/json": {
50381
- data: {
50382
- /** @description False while the clinic never said whether it charges the evaluation. */
50383
- declared: boolean;
50384
- charged: boolean | null;
50385
- amount_clp: number | null;
50386
- waived_on_treatment_start: boolean;
50387
- /** @description The convenios with a free evaluation that still exist. */
50388
- free_agreements: {
50389
- /** Format: uuid */
50390
- id: string;
50391
- name: string;
50392
- }[];
50393
- /** @description What `amount_clp: null` means for this clinic: the priced evaluation prestaciones (`is_entry`) in the aranceles the agent quotes, cheapest first (at most 20 listed). Null when none is priced. */
50394
- default_amount: {
50395
- min_clp: number;
50396
- max_clp: number;
50397
- services: {
50398
- /** Format: uuid */
50399
- id: string;
50400
- nombre: string;
50401
- categoria: string | null;
50402
- precio_clp: number;
50403
- }[];
50404
- service_count: number;
50405
- } | null;
50406
- /** @description The clinic's active convenios — the options for `free_agreement_ids`. */
50407
- agreements: {
50408
- /** Format: uuid */
50409
- id: string;
50410
- name: string;
50411
- }[];
50412
- };
51059
+ data?: unknown;
51060
+ };
51061
+ };
51062
+ };
51063
+ /** @description Validation error */
51064
+ 400: {
51065
+ headers: {
51066
+ [name: string]: unknown;
51067
+ };
51068
+ content: {
51069
+ "application/json": components["schemas"]["Error"];
51070
+ };
51071
+ };
51072
+ /** @description Unauthorized */
51073
+ 401: {
51074
+ headers: {
51075
+ [name: string]: unknown;
51076
+ };
51077
+ content: {
51078
+ "application/json": components["schemas"]["Error"];
51079
+ };
51080
+ };
51081
+ /** @description Forbidden */
51082
+ 403: {
51083
+ headers: {
51084
+ [name: string]: unknown;
51085
+ };
51086
+ content: {
51087
+ "application/json": components["schemas"]["Error"];
51088
+ };
51089
+ };
51090
+ /** @description Not found */
51091
+ 404: {
51092
+ headers: {
51093
+ [name: string]: unknown;
51094
+ };
51095
+ content: {
51096
+ "application/json": components["schemas"]["Error"];
51097
+ };
51098
+ };
51099
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
51100
+ 409: {
51101
+ headers: {
51102
+ [name: string]: unknown;
51103
+ };
51104
+ content: {
51105
+ "application/json": components["schemas"]["Error"];
51106
+ };
51107
+ };
51108
+ /** @description Rate limited */
51109
+ 429: {
51110
+ headers: {
51111
+ [name: string]: unknown;
51112
+ };
51113
+ content: {
51114
+ "application/json": components["schemas"]["Error"];
51115
+ };
51116
+ };
51117
+ };
51118
+ };
51119
+ delete?: never;
51120
+ options?: never;
51121
+ head?: never;
51122
+ patch?: never;
51123
+ trace?: never;
51124
+ };
51125
+ "/clinic/lineas/{id}": {
51126
+ parameters: {
51127
+ query?: never;
51128
+ header?: never;
51129
+ path?: never;
51130
+ cookie?: never;
51131
+ };
51132
+ get?: never;
51133
+ put?: never;
51134
+ post?: never;
51135
+ delete?: never;
51136
+ options?: never;
51137
+ head?: never;
51138
+ /**
51139
+ * Rename or archive a Línea
51140
+ * @description An archived Línea leaves the pickers but keeps the leads that carry it. The default Línea («General») cannot be archived. Requires `clinic_admin:write`.
51141
+ *
51142
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
51143
+ */
51144
+ patch: {
51145
+ parameters: {
51146
+ query?: never;
51147
+ header?: never;
51148
+ path: {
51149
+ id: string;
51150
+ };
51151
+ cookie?: never;
51152
+ };
51153
+ requestBody?: {
51154
+ content: {
51155
+ /**
51156
+ * @example {
51157
+ * "nombre": "Implantología"
51158
+ * }
51159
+ */
51160
+ "application/json": {
51161
+ nombre?: string;
51162
+ archived?: boolean;
51163
+ };
51164
+ };
51165
+ };
51166
+ responses: {
51167
+ /** @description Línea */
51168
+ 200: {
51169
+ headers: {
51170
+ [name: string]: unknown;
51171
+ };
51172
+ content: {
51173
+ /**
51174
+ * @example {
51175
+ * "data": {
51176
+ * "id": "5b1f0c1e-0000-4000-8000-000000000001",
51177
+ * "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
51178
+ * "nombre": "Implantes",
51179
+ * "is_default": false,
51180
+ * "archived_at": null,
51181
+ * "created_at": "2026-10-09T12:00:00.000Z",
51182
+ * "updated_at": "2026-10-09T12:00:00.000Z"
51183
+ * }
51184
+ * }
51185
+ */
51186
+ "application/json": {
51187
+ data?: unknown;
51188
+ meta?: {
51189
+ [key: string]: unknown;
51190
+ };
51191
+ };
51192
+ };
51193
+ };
51194
+ /** @description Validation error */
51195
+ 400: {
51196
+ headers: {
51197
+ [name: string]: unknown;
51198
+ };
51199
+ content: {
51200
+ "application/json": components["schemas"]["Error"];
51201
+ };
51202
+ };
51203
+ /** @description Unauthorized */
51204
+ 401: {
51205
+ headers: {
51206
+ [name: string]: unknown;
51207
+ };
51208
+ content: {
51209
+ "application/json": components["schemas"]["Error"];
51210
+ };
51211
+ };
51212
+ /** @description Forbidden */
51213
+ 403: {
51214
+ headers: {
51215
+ [name: string]: unknown;
51216
+ };
51217
+ content: {
51218
+ "application/json": components["schemas"]["Error"];
51219
+ };
51220
+ };
51221
+ /** @description Not found */
51222
+ 404: {
51223
+ headers: {
51224
+ [name: string]: unknown;
51225
+ };
51226
+ content: {
51227
+ "application/json": components["schemas"]["Error"];
51228
+ };
51229
+ };
51230
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
51231
+ 409: {
51232
+ headers: {
51233
+ [name: string]: unknown;
51234
+ };
51235
+ content: {
51236
+ "application/json": components["schemas"]["Error"];
51237
+ };
51238
+ };
51239
+ /** @description Rate limited */
51240
+ 429: {
51241
+ headers: {
51242
+ [name: string]: unknown;
51243
+ };
51244
+ content: {
51245
+ "application/json": components["schemas"]["Error"];
51246
+ };
51247
+ };
51248
+ };
51249
+ };
51250
+ trace?: never;
51251
+ };
51252
+ "/clinic/lineas/{id}/merge": {
51253
+ parameters: {
51254
+ query?: never;
51255
+ header?: never;
51256
+ path?: never;
51257
+ cookie?: never;
51258
+ };
51259
+ get?: never;
51260
+ put?: never;
51261
+ /**
51262
+ * Merge a Línea into another
51263
+ * @description Moves every lead, specialty and rule of this Línea to `into_id`, then removes it. Irreversible. Requires `clinic_admin:write`.
51264
+ *
51265
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
51266
+ */
51267
+ post: {
51268
+ parameters: {
51269
+ query?: never;
51270
+ header?: {
51271
+ /** @description Replay-safe retries: resending the SAME key with the SAME body returns the original response (`X-Idempotent-Replay: 1`) instead of creating a second copy — safe to send whenever a response might not have arrived. The same key with a DIFFERENT body answers `409 IDEMPOTENCY_KEY_CONFLICT`; use a fresh key per operation. */
51272
+ "Idempotency-Key"?: string;
51273
+ };
51274
+ path: {
51275
+ id: string;
51276
+ };
51277
+ cookie?: never;
51278
+ };
51279
+ requestBody?: {
51280
+ content: {
51281
+ /**
51282
+ * @example {
51283
+ * "into_id": "5b1f0c1e-0000-4000-8000-000000000001"
51284
+ * }
51285
+ */
51286
+ "application/json": {
51287
+ /** Format: uuid */
51288
+ into_id: string;
51289
+ };
51290
+ };
51291
+ };
51292
+ responses: {
51293
+ /** @description Merged */
51294
+ 200: {
51295
+ headers: {
51296
+ [name: string]: unknown;
51297
+ };
51298
+ content: {
51299
+ /**
51300
+ * @example {
51301
+ * "data": {
51302
+ * "into": {
51303
+ * "id": "5b1f0c1e-0000-4000-8000-000000000001",
51304
+ * "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
51305
+ * "nombre": "Implantes",
51306
+ * "is_default": false,
51307
+ * "archived_at": null,
51308
+ * "created_at": "2026-10-09T12:00:00.000Z",
51309
+ * "updated_at": "2026-10-09T12:00:00.000Z"
51310
+ * },
51311
+ * "moved": {
51312
+ * "leads": 4,
51313
+ * "specialties": 1,
51314
+ * "rules": 2
51315
+ * }
51316
+ * }
51317
+ * }
51318
+ */
51319
+ "application/json": {
51320
+ data?: unknown;
51321
+ meta?: {
51322
+ [key: string]: unknown;
51323
+ };
51324
+ };
51325
+ };
51326
+ };
51327
+ /** @description Validation error */
51328
+ 400: {
51329
+ headers: {
51330
+ [name: string]: unknown;
51331
+ };
51332
+ content: {
51333
+ "application/json": components["schemas"]["Error"];
51334
+ };
51335
+ };
51336
+ /** @description Unauthorized */
51337
+ 401: {
51338
+ headers: {
51339
+ [name: string]: unknown;
51340
+ };
51341
+ content: {
51342
+ "application/json": components["schemas"]["Error"];
51343
+ };
51344
+ };
51345
+ /** @description Forbidden */
51346
+ 403: {
51347
+ headers: {
51348
+ [name: string]: unknown;
51349
+ };
51350
+ content: {
51351
+ "application/json": components["schemas"]["Error"];
51352
+ };
51353
+ };
51354
+ /** @description Not found */
51355
+ 404: {
51356
+ headers: {
51357
+ [name: string]: unknown;
51358
+ };
51359
+ content: {
51360
+ "application/json": components["schemas"]["Error"];
51361
+ };
51362
+ };
51363
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
51364
+ 409: {
51365
+ headers: {
51366
+ [name: string]: unknown;
51367
+ };
51368
+ content: {
51369
+ "application/json": components["schemas"]["Error"];
51370
+ };
51371
+ };
51372
+ /** @description Rate limited */
51373
+ 429: {
51374
+ headers: {
51375
+ [name: string]: unknown;
51376
+ };
51377
+ content: {
51378
+ "application/json": components["schemas"]["Error"];
51379
+ };
51380
+ };
51381
+ };
51382
+ };
51383
+ delete?: never;
51384
+ options?: never;
51385
+ head?: never;
51386
+ patch?: never;
51387
+ trace?: never;
51388
+ };
51389
+ "/clinic/lineas/specialties/{id}": {
51390
+ parameters: {
51391
+ query?: never;
51392
+ header?: never;
51393
+ path?: never;
51394
+ cookie?: never;
51395
+ };
51396
+ get?: never;
51397
+ /**
51398
+ * Place a roster specialty in a Línea
51399
+ * @description `id` is a `clinic_specialty` id. `linea_id: null` takes it out of its Línea. Requires `clinic_admin:write`.
51400
+ *
51401
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
51402
+ */
51403
+ put: {
51404
+ parameters: {
51405
+ query?: never;
51406
+ header?: never;
51407
+ path: {
51408
+ id: string;
51409
+ };
51410
+ cookie?: never;
51411
+ };
51412
+ requestBody?: {
51413
+ content: {
51414
+ /**
51415
+ * @example {
51416
+ * "linea_id": "5b1f0c1e-0000-4000-8000-000000000001"
51417
+ * }
51418
+ */
51419
+ "application/json": {
51420
+ /**
51421
+ * Format: uuid
51422
+ * @description `null` takes the specialty out of its Línea (unmapped).
51423
+ */
51424
+ linea_id: string | null;
51425
+ };
51426
+ };
51427
+ };
51428
+ responses: {
51429
+ /** @description Placed */
51430
+ 200: {
51431
+ headers: {
51432
+ [name: string]: unknown;
51433
+ };
51434
+ content: {
51435
+ /**
51436
+ * @example {
51437
+ * "data": {
51438
+ * "id": "5b1f0c1e-0000-4000-8000-000000000002",
51439
+ * "linea_id": "5b1f0c1e-0000-4000-8000-000000000001"
51440
+ * }
51441
+ * }
51442
+ */
51443
+ "application/json": {
51444
+ data?: unknown;
51445
+ meta?: {
51446
+ [key: string]: unknown;
51447
+ };
51448
+ };
51449
+ };
51450
+ };
51451
+ /** @description Validation error */
51452
+ 400: {
51453
+ headers: {
51454
+ [name: string]: unknown;
51455
+ };
51456
+ content: {
51457
+ "application/json": components["schemas"]["Error"];
51458
+ };
51459
+ };
51460
+ /** @description Unauthorized */
51461
+ 401: {
51462
+ headers: {
51463
+ [name: string]: unknown;
51464
+ };
51465
+ content: {
51466
+ "application/json": components["schemas"]["Error"];
51467
+ };
51468
+ };
51469
+ /** @description Forbidden */
51470
+ 403: {
51471
+ headers: {
51472
+ [name: string]: unknown;
51473
+ };
51474
+ content: {
51475
+ "application/json": components["schemas"]["Error"];
51476
+ };
51477
+ };
51478
+ /** @description Not found */
51479
+ 404: {
51480
+ headers: {
51481
+ [name: string]: unknown;
51482
+ };
51483
+ content: {
51484
+ "application/json": components["schemas"]["Error"];
51485
+ };
51486
+ };
51487
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
51488
+ 409: {
51489
+ headers: {
51490
+ [name: string]: unknown;
51491
+ };
51492
+ content: {
51493
+ "application/json": components["schemas"]["Error"];
51494
+ };
51495
+ };
51496
+ /** @description Rate limited */
51497
+ 429: {
51498
+ headers: {
51499
+ [name: string]: unknown;
51500
+ };
51501
+ content: {
51502
+ "application/json": components["schemas"]["Error"];
51503
+ };
51504
+ };
51505
+ };
51506
+ };
51507
+ post?: never;
51508
+ delete?: never;
51509
+ options?: never;
51510
+ head?: never;
51511
+ patch?: never;
51512
+ trace?: never;
51513
+ };
51514
+ "/clinic/lineas/{id}/rules": {
51515
+ parameters: {
51516
+ query?: never;
51517
+ header?: never;
51518
+ path?: never;
51519
+ cookie?: never;
51520
+ };
51521
+ get?: never;
51522
+ put?: never;
51523
+ /**
51524
+ * Map ads to a Línea by a name fragment
51525
+ * @description An ad whose campaign, ad set or ad name contains the fragment belongs to this Línea; the longest fragment wins. A fragment already pointing at another Línea answers 409. Requires `clinic_admin:write`.
51526
+ *
51527
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
51528
+ */
51529
+ post: {
51530
+ parameters: {
51531
+ query?: never;
51532
+ header?: {
51533
+ /** @description Replay-safe retries: resending the SAME key with the SAME body returns the original response (`X-Idempotent-Replay: 1`) instead of creating a second copy — safe to send whenever a response might not have arrived. The same key with a DIFFERENT body answers `409 IDEMPOTENCY_KEY_CONFLICT`; use a fresh key per operation. */
51534
+ "Idempotency-Key"?: string;
51535
+ };
51536
+ path: {
51537
+ id: string;
51538
+ };
51539
+ cookie?: never;
51540
+ };
51541
+ requestBody?: {
51542
+ content: {
51543
+ /**
51544
+ * @example {
51545
+ * "match": "GABRIELA RIQUELME"
51546
+ * }
51547
+ */
51548
+ "application/json": {
51549
+ /** @description A fragment of a campaign, ad set or ad name (accent- and case-insensitive), e.g. `IMPLANT` or an ad set named after a professional. */
51550
+ match: string;
51551
+ };
51552
+ };
51553
+ };
51554
+ responses: {
51555
+ /** @description Rule */
51556
+ 201: {
51557
+ headers: {
51558
+ [name: string]: unknown;
51559
+ };
51560
+ content: {
51561
+ /**
51562
+ * @example {
51563
+ * "data": {
51564
+ * "id": "5b1f0c1e-0000-4000-8000-000000000003",
51565
+ * "tenant_id": "5b1f0c1e-0000-4000-8000-0000000000aa",
51566
+ * "linea_id": "5b1f0c1e-0000-4000-8000-000000000001",
51567
+ * "match": "GABRIELA RIQUELME",
51568
+ * "source": "owner",
51569
+ * "created_at": "2026-10-09T12:00:00.000Z"
51570
+ * }
51571
+ * }
51572
+ */
51573
+ "application/json": {
51574
+ data?: unknown;
51575
+ };
51576
+ };
51577
+ };
51578
+ /** @description Validation error */
51579
+ 400: {
51580
+ headers: {
51581
+ [name: string]: unknown;
51582
+ };
51583
+ content: {
51584
+ "application/json": components["schemas"]["Error"];
51585
+ };
51586
+ };
51587
+ /** @description Unauthorized */
51588
+ 401: {
51589
+ headers: {
51590
+ [name: string]: unknown;
51591
+ };
51592
+ content: {
51593
+ "application/json": components["schemas"]["Error"];
51594
+ };
51595
+ };
51596
+ /** @description Forbidden */
51597
+ 403: {
51598
+ headers: {
51599
+ [name: string]: unknown;
51600
+ };
51601
+ content: {
51602
+ "application/json": components["schemas"]["Error"];
51603
+ };
51604
+ };
51605
+ /** @description Not found */
51606
+ 404: {
51607
+ headers: {
51608
+ [name: string]: unknown;
51609
+ };
51610
+ content: {
51611
+ "application/json": components["schemas"]["Error"];
51612
+ };
51613
+ };
51614
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
51615
+ 409: {
51616
+ headers: {
51617
+ [name: string]: unknown;
51618
+ };
51619
+ content: {
51620
+ "application/json": components["schemas"]["Error"];
51621
+ };
51622
+ };
51623
+ /** @description Rate limited */
51624
+ 429: {
51625
+ headers: {
51626
+ [name: string]: unknown;
51627
+ };
51628
+ content: {
51629
+ "application/json": components["schemas"]["Error"];
51630
+ };
51631
+ };
51632
+ };
51633
+ };
51634
+ delete?: never;
51635
+ options?: never;
51636
+ head?: never;
51637
+ patch?: never;
51638
+ trace?: never;
51639
+ };
51640
+ "/clinic/lineas/rules/{id}": {
51641
+ parameters: {
51642
+ query?: never;
51643
+ header?: never;
51644
+ path?: never;
51645
+ cookie?: never;
51646
+ };
51647
+ get?: never;
51648
+ put?: never;
51649
+ post?: never;
51650
+ /**
51651
+ * Remove a name fragment rule
51652
+ * @description Requires `clinic_admin:write`.
51653
+ *
51654
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
51655
+ */
51656
+ delete: {
51657
+ parameters: {
51658
+ query?: never;
51659
+ header?: never;
51660
+ path: {
51661
+ id: string;
51662
+ };
51663
+ cookie?: never;
51664
+ };
51665
+ requestBody?: never;
51666
+ responses: {
51667
+ /** @description No content */
51668
+ 204: {
51669
+ headers: {
51670
+ [name: string]: unknown;
51671
+ };
51672
+ content?: never;
51673
+ };
51674
+ /** @description Validation error */
51675
+ 400: {
51676
+ headers: {
51677
+ [name: string]: unknown;
51678
+ };
51679
+ content: {
51680
+ "application/json": components["schemas"]["Error"];
51681
+ };
51682
+ };
51683
+ /** @description Unauthorized */
51684
+ 401: {
51685
+ headers: {
51686
+ [name: string]: unknown;
51687
+ };
51688
+ content: {
51689
+ "application/json": components["schemas"]["Error"];
51690
+ };
51691
+ };
51692
+ /** @description Forbidden */
51693
+ 403: {
51694
+ headers: {
51695
+ [name: string]: unknown;
51696
+ };
51697
+ content: {
51698
+ "application/json": components["schemas"]["Error"];
51699
+ };
51700
+ };
51701
+ /** @description Not found */
51702
+ 404: {
51703
+ headers: {
51704
+ [name: string]: unknown;
51705
+ };
51706
+ content: {
51707
+ "application/json": components["schemas"]["Error"];
51708
+ };
51709
+ };
51710
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
51711
+ 409: {
51712
+ headers: {
51713
+ [name: string]: unknown;
51714
+ };
51715
+ content: {
51716
+ "application/json": components["schemas"]["Error"];
51717
+ };
51718
+ };
51719
+ /** @description Rate limited */
51720
+ 429: {
51721
+ headers: {
51722
+ [name: string]: unknown;
51723
+ };
51724
+ content: {
51725
+ "application/json": components["schemas"]["Error"];
51726
+ };
51727
+ };
51728
+ };
51729
+ };
51730
+ options?: never;
51731
+ head?: never;
51732
+ patch?: never;
51733
+ trace?: never;
51734
+ };
51735
+ "/clinic/evaluation": {
51736
+ parameters: {
51737
+ query?: never;
51738
+ header?: never;
51739
+ path?: never;
51740
+ cookie?: never;
51741
+ };
51742
+ /**
51743
+ * The clinic's «Evaluación»
51744
+ * @description Clinic data the per-agent price policy reads: whether the clinic charges the evaluation (`declared: false` while it never said), a single amount (`null` = each specialty's evaluation prestación price, summarised in `default_amount`), whether it is waived when the patient starts treatment, and the convenios with a free evaluation. It reaches an agent's prompt only when that agent's `behavior_policies.treatment_price` is set. Requires `clinic:read`.
51745
+ *
51746
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
51747
+ */
51748
+ get: {
51749
+ parameters: {
51750
+ query?: never;
51751
+ header?: never;
51752
+ path?: never;
51753
+ cookie?: never;
51754
+ };
51755
+ requestBody?: never;
51756
+ responses: {
51757
+ /** @description The evaluation settings */
51758
+ 200: {
51759
+ headers: {
51760
+ [name: string]: unknown;
51761
+ };
51762
+ content: {
51763
+ /**
51764
+ * @example {
51765
+ * "data": {
51766
+ * "declared": true,
51767
+ * "charged": true,
51768
+ * "amount_clp": null,
51769
+ * "waived_on_treatment_start": true,
51770
+ * "free_agreements": [
51771
+ * {
51772
+ * "id": "7c1e2f4a-0000-4000-8000-000000000001",
51773
+ * "name": "Convenio Club Deportivo"
51774
+ * }
51775
+ * ],
51776
+ * "default_amount": {
51777
+ * "min_clp": 15000,
51778
+ * "max_clp": 50000,
51779
+ * "services": [
51780
+ * {
51781
+ * "id": "5d2b9e10-0000-4000-8000-000000000001",
51782
+ * "nombre": "Evaluación medicina estética facial",
51783
+ * "categoria": null,
51784
+ * "precio_clp": 15000
51785
+ * },
51786
+ * {
51787
+ * "id": "5d2b9e10-0000-4000-8000-000000000002",
51788
+ * "nombre": "Evaluación y diagnóstico dental inicial",
51789
+ * "categoria": null,
51790
+ * "precio_clp": 50000
51791
+ * }
51792
+ * ],
51793
+ * "service_count": 2
51794
+ * },
51795
+ * "agreements": [
51796
+ * {
51797
+ * "id": "7c1e2f4a-0000-4000-8000-000000000001",
51798
+ * "name": "Convenio Club Deportivo"
51799
+ * }
51800
+ * ]
51801
+ * }
51802
+ * }
51803
+ */
51804
+ "application/json": {
51805
+ data: {
51806
+ /** @description False while the clinic never said whether it charges the evaluation. */
51807
+ declared: boolean;
51808
+ charged: boolean | null;
51809
+ amount_clp: number | null;
51810
+ waived_on_treatment_start: boolean;
51811
+ /** @description The convenios with a free evaluation that still exist. */
51812
+ free_agreements: {
51813
+ /** Format: uuid */
51814
+ id: string;
51815
+ name: string;
51816
+ }[];
51817
+ /** @description What `amount_clp: null` means for this clinic: the priced evaluation prestaciones (`is_entry`) in the aranceles the agent quotes, cheapest first (at most 20 listed). Null when none is priced. */
51818
+ default_amount: {
51819
+ min_clp: number;
51820
+ max_clp: number;
51821
+ services: {
51822
+ /** Format: uuid */
51823
+ id: string;
51824
+ nombre: string;
51825
+ categoria: string | null;
51826
+ precio_clp: number;
51827
+ }[];
51828
+ service_count: number;
51829
+ } | null;
51830
+ /** @description The clinic's active convenios — the options for `free_agreement_ids`. */
51831
+ agreements: {
51832
+ /** Format: uuid */
51833
+ id: string;
51834
+ name: string;
51835
+ }[];
51836
+ };
51837
+ };
51838
+ };
51839
+ };
51840
+ /** @description Validation error */
51841
+ 400: {
51842
+ headers: {
51843
+ [name: string]: unknown;
51844
+ };
51845
+ content: {
51846
+ "application/json": components["schemas"]["Error"];
51847
+ };
51848
+ };
51849
+ /** @description Unauthorized */
51850
+ 401: {
51851
+ headers: {
51852
+ [name: string]: unknown;
51853
+ };
51854
+ content: {
51855
+ "application/json": components["schemas"]["Error"];
51856
+ };
51857
+ };
51858
+ /** @description Forbidden */
51859
+ 403: {
51860
+ headers: {
51861
+ [name: string]: unknown;
51862
+ };
51863
+ content: {
51864
+ "application/json": components["schemas"]["Error"];
51865
+ };
51866
+ };
51867
+ /** @description Not found */
51868
+ 404: {
51869
+ headers: {
51870
+ [name: string]: unknown;
51871
+ };
51872
+ content: {
51873
+ "application/json": components["schemas"]["Error"];
51874
+ };
51875
+ };
51876
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
51877
+ 409: {
51878
+ headers: {
51879
+ [name: string]: unknown;
51880
+ };
51881
+ content: {
51882
+ "application/json": components["schemas"]["Error"];
51883
+ };
51884
+ };
51885
+ /** @description Rate limited */
51886
+ 429: {
51887
+ headers: {
51888
+ [name: string]: unknown;
51889
+ };
51890
+ content: {
51891
+ "application/json": components["schemas"]["Error"];
51892
+ };
51893
+ };
51894
+ };
51895
+ };
51896
+ /**
51897
+ * Update the clinic's «Evaluación»
51898
+ * @description Partial: merges field by field over what is stored. `free_agreement_ids` must be convenios of this workspace (400 otherwise). Returns the same view as the GET. Audited. Requires `clinic_admin:write`.
51899
+ *
51900
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
51901
+ */
51902
+ put: {
51903
+ parameters: {
51904
+ query?: never;
51905
+ header?: never;
51906
+ path?: never;
51907
+ cookie?: never;
51908
+ };
51909
+ requestBody?: {
51910
+ content: {
51911
+ /**
51912
+ * @example {
51913
+ * "charged": true,
51914
+ * "waived_on_treatment_start": true
51915
+ * }
51916
+ */
51917
+ "application/json": {
51918
+ /** @description Whether the clinic charges the evaluation. */
51919
+ charged?: boolean;
51920
+ /** @description One amount in whole CLP for every evaluation. Null = each specialty's evaluation prestación (`is_entry`) price from the catalogue. */
51921
+ amount_clp?: number | null;
51922
+ /** @description «No se cobra si inicia tratamiento»: the evaluation is credited to the treatment when the patient starts it. */
51923
+ waived_on_treatment_start?: boolean;
51924
+ /** @description Convenios (`clinic_agreement` ids) whose members get the evaluation free. */
51925
+ free_agreement_ids?: string[];
51926
+ };
51927
+ };
51928
+ };
51929
+ responses: {
51930
+ /** @description The updated evaluation settings */
51931
+ 200: {
51932
+ headers: {
51933
+ [name: string]: unknown;
51934
+ };
51935
+ content: {
51936
+ /**
51937
+ * @example {
51938
+ * "data": {
51939
+ * "declared": true,
51940
+ * "charged": true,
51941
+ * "amount_clp": null,
51942
+ * "waived_on_treatment_start": true,
51943
+ * "free_agreements": [
51944
+ * {
51945
+ * "id": "7c1e2f4a-0000-4000-8000-000000000001",
51946
+ * "name": "Convenio Club Deportivo"
51947
+ * }
51948
+ * ],
51949
+ * "default_amount": {
51950
+ * "min_clp": 15000,
51951
+ * "max_clp": 50000,
51952
+ * "services": [
51953
+ * {
51954
+ * "id": "5d2b9e10-0000-4000-8000-000000000001",
51955
+ * "nombre": "Evaluación medicina estética facial",
51956
+ * "categoria": null,
51957
+ * "precio_clp": 15000
51958
+ * },
51959
+ * {
51960
+ * "id": "5d2b9e10-0000-4000-8000-000000000002",
51961
+ * "nombre": "Evaluación y diagnóstico dental inicial",
51962
+ * "categoria": null,
51963
+ * "precio_clp": 50000
51964
+ * }
51965
+ * ],
51966
+ * "service_count": 2
51967
+ * },
51968
+ * "agreements": [
51969
+ * {
51970
+ * "id": "7c1e2f4a-0000-4000-8000-000000000001",
51971
+ * "name": "Convenio Club Deportivo"
51972
+ * }
51973
+ * ]
51974
+ * }
51975
+ * }
51976
+ */
51977
+ "application/json": {
51978
+ data: {
51979
+ /** @description False while the clinic never said whether it charges the evaluation. */
51980
+ declared: boolean;
51981
+ charged: boolean | null;
51982
+ amount_clp: number | null;
51983
+ waived_on_treatment_start: boolean;
51984
+ /** @description The convenios with a free evaluation that still exist. */
51985
+ free_agreements: {
51986
+ /** Format: uuid */
51987
+ id: string;
51988
+ name: string;
51989
+ }[];
51990
+ /** @description What `amount_clp: null` means for this clinic: the priced evaluation prestaciones (`is_entry`) in the aranceles the agent quotes, cheapest first (at most 20 listed). Null when none is priced. */
51991
+ default_amount: {
51992
+ min_clp: number;
51993
+ max_clp: number;
51994
+ services: {
51995
+ /** Format: uuid */
51996
+ id: string;
51997
+ nombre: string;
51998
+ categoria: string | null;
51999
+ precio_clp: number;
52000
+ }[];
52001
+ service_count: number;
52002
+ } | null;
52003
+ /** @description The clinic's active convenios — the options for `free_agreement_ids`. */
52004
+ agreements: {
52005
+ /** Format: uuid */
52006
+ id: string;
52007
+ name: string;
52008
+ }[];
52009
+ };
50413
52010
  };
50414
52011
  };
50415
52012
  };
@@ -71445,7 +73042,7 @@ interface paths {
71445
73042
  };
71446
73043
  /**
71447
73044
  * Agenda for a window, any engine, by professional and branch
71448
- * @description Mirrored AND native appointments between `from` (inclusive) and `to` (EXCLUSIVE), bare `YYYY-MM-DD` days in the clinic's timezone; the window may not exceed 8 days. Cancelled citas are INCLUDED (the grid draws them muted). Unlike `GET /clinic/agenda`, `professional_id` and `location_id` are VITRINA uuids — `clinic_professional.id` and `location.id` — so one query serves a Medilink, a Reservo and a native clinic. Each row still carries the vendor's own ids under `vendor` for provenance: a row with `vendor.professional_id` set and `professional: null` is a roster gap, never a cita nobody is attending. Never calls the clinic API. `flags` are the clinical alerts the clinic shows on the block: clinical-record content, returned to an API key or personal token only when it also holds `clinic_record:read`.
73045
+ * @description Mirrored AND native appointments between `from` (inclusive) and `to` (EXCLUSIVE), bare `YYYY-MM-DD` days in the clinic's timezone; the window may not exceed 8 days. Cancelled citas are INCLUDED (the grid draws them muted). Unlike `GET /clinic/agenda`, `professional_id` and `location_id` are VITRINA uuids — `clinic_professional.id` and `location.id` — so one query serves a Medilink, a Reservo and a native clinic. Each row still carries the vendor's own ids under `vendor` for provenance: a row with `vendor.professional_id` set and `professional: null` is a roster gap, never a cita nobody is attending. Never calls the clinic API. `flags` are the clinical alerts the clinic shows on the block: clinical-record content, returned to an API key or personal token only when it also holds `clinic_record:read`. `booking_kind` is the cita's Tipo de agendamiento: `new` (Nuevo: it would be a primera visita and the patient never booked before), `new_rebooked` (Nuevo · reagenda: a primera visita, but the patient had earlier bookings and never came) or `in_treatment` (En tratamiento: every other cita); null on a row that is not a patient's cita. It uses the primera-visita rule Vitrina Ads counts, so a patient back after 12 months without an attended cita is `new`.
71449
73046
  *
71450
73047
  * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
71451
73048
  */
@@ -71510,6 +73107,7 @@ interface paths {
71510
73107
  * "customer_name": "María José Fuentes Lagos",
71511
73108
  * "notes": "Avisar al +56987654321 si se atrasa",
71512
73109
  * "money": null,
73110
+ * "booking_kind": "new",
71513
73111
  * "flags": [
71514
73112
  * {
71515
73113
  * "id": "13131313-0000-4000-8000-000000000001",
@@ -84574,7 +86172,679 @@ interface paths {
84574
86172
  };
84575
86173
  };
84576
86174
  };
84577
- put?: never;
86175
+ put?: never;
86176
+ post?: never;
86177
+ delete?: never;
86178
+ options?: never;
86179
+ head?: never;
86180
+ patch?: never;
86181
+ trace?: never;
86182
+ };
86183
+ "/clinic/insights/comercial": {
86184
+ parameters: {
86185
+ query?: never;
86186
+ header?: never;
86187
+ path?: never;
86188
+ cookie?: never;
86189
+ };
86190
+ /**
86191
+ * Commercial numbers: presupuesto acceptance, cierres, cash, botados
86192
+ * @description The clinic's commercial numbers, aggregates only (counts, CLP, percentages, months and professional names; never a patient). `regime` says how acceptance is read: `evidencia` when the revenue mirror is on (a non-entry line performed, or a cita booked after the presupuesto, inside its window), `explicita` otherwise (the acceptance the clinic recorded). Acceptance is by COUNT of presupuestos, one per lineage; $0 and entry-only presupuestos are out of both sides; the window is 60 days (90 for ortodoncia and implantología); the rate goes to the professional who issued the presupuesto. Presupuestos ISSUED in the window form the acceptance cohort (`cohorts` by issue month with 30- and 60-day reads). A cierre is the first payment on an accepted presupuesto: once per presupuesto, dated on that payment, valued at the full accepted value. `headline` is the cash collected in the window against the previous window of the same length. `first_visit` (new patients who paid more than the evaluación the day of their first attended cita; `null` with `reason` when no evaluación is priced), `second_visit` (accepted presupuestos captured after the issue day) and `botados` (two lists — «nunca aceptó» and «quedó a medias», 90 days without cita or pago and no future cita, over the presupuestos of the last 12 months, per issuing professional) carry `formula: mentores_2026_09_29`. An acceptance proven by a cita stays accepted when the cita is later cancelled (`cita_only` counts them). `acceptance.reference` and each `by_professional[].reference` place the rate on the mentors' «rango de referencia del programa» (the dentist's especialidad range, else general; `null` when none). Every KPI carries its `formula` and a printable `rule`; a rate with no denominator is `null`, never 0. `state` is `empty`, `settling` (presupuesto lines still being read; `state_reason` says so) or `ready`. Requires `clinic_money:read` (or `clinic_insights:read`, which reads the same aggregates). Accepts `from` / `to` (bare `YYYY-MM-DD`, `to` INCLUSIVE; default the 90 days ending today in the clinic's zone), `location_id` and `professional_id`. With `professional_id`, cash (`headline`, `months[].collected_clp`) is `null`: a payment belongs to the patient, not to a professional. A caller WITHOUT `clinic_money:read` always reads one fixed window — the last 3 closed calendar months — whatever range or filter it asks (`suppression.filters_ignored`), with every count or CLP resting on 1–4 cases `null`, rates over fewer than 5 cases `null`, complementary suppression over professionals, cohorts and botados lists, and month rows carrying only cash.
86193
+ *
86194
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
86195
+ */
86196
+ get: {
86197
+ parameters: {
86198
+ query?: {
86199
+ /** @description First day of the window, inclusive. Defaults to 90 days before `to`. */
86200
+ from?: string;
86201
+ /** @description Last day of the window, INCLUSIVE. Defaults to today in the clinic's zone. */
86202
+ to?: string;
86203
+ /** @description One sucursal. Omit for all of them. */
86204
+ location_id?: string;
86205
+ /** @description One profesional. A foreign id is a 404, never an empty report. */
86206
+ professional_id?: string;
86207
+ };
86208
+ header?: never;
86209
+ path?: never;
86210
+ cookie?: never;
86211
+ };
86212
+ requestBody?: never;
86213
+ responses: {
86214
+ /** @description Clinic commercial numbers */
86215
+ 200: {
86216
+ headers: {
86217
+ [name: string]: unknown;
86218
+ };
86219
+ content: {
86220
+ /**
86221
+ * @example {
86222
+ * "data": {
86223
+ * "from": "2026-06-01",
86224
+ * "to": "2026-08-29",
86225
+ * "timezone": "America/Santiago",
86226
+ * "location_id": null,
86227
+ * "professional_id": null,
86228
+ * "state": "ready",
86229
+ * "state_reason": null,
86230
+ * "regime": "evidencia",
86231
+ * "regime_label": "Aceptación por evidencia",
86232
+ * "regime_rule": "Leído desde tu software de agenda: un presupuesto cuenta como aceptado cuando se realizó una de sus prestaciones o el paciente quedó con una cita agendada después de recibirlo, aunque después la cancele.",
86233
+ * "source": {
86234
+ * "kind": "mirror",
86235
+ * "synced_at": "2026-08-30T03:10:00.000Z"
86236
+ * },
86237
+ * "formulas": {
86238
+ * "acceptance": "mentores_2026_09_25",
86239
+ * "cierre": "cierre_2026_09_25",
86240
+ * "first_visit": "mentores_2026_09_29",
86241
+ * "second_visit": "mentores_2026_09_29",
86242
+ * "botados": "mentores_2026_09_29"
86243
+ * },
86244
+ * "headline": {
86245
+ * "collected_clp": 48500000,
86246
+ * "previous_collected_clp": 44100000,
86247
+ * "delta_pct": 10,
86248
+ * "previous_from": "2026-03-03",
86249
+ * "previous_to": "2026-05-31",
86250
+ * "rule": "Lo cobrado en el período: todos los pagos recibidos, sin los anulados. Se compara con el período anterior del mismo largo."
86251
+ * },
86252
+ * "acceptance": {
86253
+ * "presented": 420,
86254
+ * "accepted": 231,
86255
+ * "rate": 55,
86256
+ * "same_day": 118,
86257
+ * "later": 113,
86258
+ * "cita_only": 14,
86259
+ * "pending": 36,
86260
+ * "presented_clp": 142000000,
86261
+ * "accepted_clp": 71300000,
86262
+ * "rate_30d": 47.2,
86263
+ * "rate_60d": 53.8,
86264
+ * "formula": "mentores_2026_09_25",
86265
+ * "window_days": 60,
86266
+ * "long_window_days": 90,
86267
+ * "rule": "Presupuestos aceptados ÷ presupuestos presentados, cada presupuesto una vez.",
86268
+ * "reference": {
86269
+ * "label": "Rango de referencia del programa",
86270
+ * "especialidad": null,
86271
+ * "band": "malo",
86272
+ * "note": null,
86273
+ * "muy_malo_upper_pct": 40,
86274
+ * "malo_upper_pct": 60,
86275
+ * "regular_upper_pct": 70,
86276
+ * "excelente_lower_pct": 90
86277
+ * }
86278
+ * },
86279
+ * "cierres": {
86280
+ * "count": 180,
86281
+ * "value_clp": 58900000,
86282
+ * "accepted_without_payment": 51,
86283
+ * "formula": "cierre_2026_09_25",
86284
+ * "rule": "Un cierre es el primer pago de un presupuesto aceptado."
86285
+ * },
86286
+ * "cohorts": [
86287
+ * {
86288
+ * "month": "2026-06",
86289
+ * "presented": 140,
86290
+ * "accepted": 80,
86291
+ * "rate": 57.1,
86292
+ * "same_day": 41,
86293
+ * "later": 39,
86294
+ * "cita_only": 2,
86295
+ * "pending": 0,
86296
+ * "presented_clp": 47000000,
86297
+ * "accepted_clp": 24800000,
86298
+ * "rate_30d": 49.3,
86299
+ * "rate_60d": 56.4,
86300
+ * "mature": true
86301
+ * }
86302
+ * ],
86303
+ * "by_professional": [
86304
+ * {
86305
+ * "professional_id": "0f3c2a9e-5b1d-4c7e-9a42-6d8e1f2b3c4d",
86306
+ * "professional_name": "Dra. Camila Soto",
86307
+ * "presented": 96,
86308
+ * "accepted": 61,
86309
+ * "rate": 63.5,
86310
+ * "same_day": 30,
86311
+ * "later": 31,
86312
+ * "cita_only": 3,
86313
+ * "pending": 8,
86314
+ * "presented_clp": 33400000,
86315
+ * "accepted_clp": 19900000,
86316
+ * "cierres": 47,
86317
+ * "cierres_clp": 15100000,
86318
+ * "reference": {
86319
+ * "label": "Rango de referencia del programa",
86320
+ * "especialidad": null,
86321
+ * "band": "regular",
86322
+ * "note": null,
86323
+ * "muy_malo_upper_pct": 40,
86324
+ * "malo_upper_pct": 60,
86325
+ * "regular_upper_pct": 70,
86326
+ * "excelente_lower_pct": 90
86327
+ * }
86328
+ * }
86329
+ * ],
86330
+ * "unassigned_presented": 0,
86331
+ * "unassigned_cierres": 0,
86332
+ * "months": [
86333
+ * {
86334
+ * "month": "2026-06",
86335
+ * "accepted_clp": 24100000,
86336
+ * "produced_clp": 19700000,
86337
+ * "collected_clp": 16200000,
86338
+ * "cierres": 62,
86339
+ * "cierres_clp": 20300000
86340
+ * }
86341
+ * ],
86342
+ * "first_visit": {
86343
+ * "formula": "mentores_2026_09_29",
86344
+ * "patients": 210,
86345
+ * "paid": 97,
86346
+ * "rate": 46.2,
86347
+ * "evaluation_price_clp": 34990,
86348
+ * "reason": null,
86349
+ * "rule": "De los pacientes nuevos cuya primera cita atendida cae en el período, la parte que ese día pagó más que la evaluación ($34.990)."
86350
+ * },
86351
+ * "second_visit": {
86352
+ * "formula": "mentores_2026_09_29",
86353
+ * "eligible": 231,
86354
+ * "converted": 113,
86355
+ * "rate": 48.9,
86356
+ * "rule": "De los presupuestos aceptados del período, la parte que se aceptó en una visita posterior y no el mismo día en que se entregó."
86357
+ * },
86358
+ * "botados": {
86359
+ * "unaccepted": {
86360
+ * "count": 88,
86361
+ * "clp": 31200000
86362
+ * },
86363
+ * "accepted_unscheduled": {
86364
+ * "count": 0,
86365
+ * "clp": 0
86366
+ * },
86367
+ * "mid_treatment_no_future_cita": {
86368
+ * "count": 23,
86369
+ * "clp": 9800000
86370
+ * },
86371
+ * "by_professional": [
86372
+ * {
86373
+ * "professional_id": "0f3c2a9e-5b1d-4c7e-9a42-6d8e1f2b3c4d",
86374
+ * "professional_name": "Dra. Camila Soto",
86375
+ * "unaccepted": {
86376
+ * "count": 21,
86377
+ * "clp": 7400000
86378
+ * },
86379
+ * "mid_treatment_no_future_cita": {
86380
+ * "count": 6,
86381
+ * "clp": 2900000
86382
+ * }
86383
+ * }
86384
+ * ],
86385
+ * "formula": "mentores_2026_09_29",
86386
+ * "basis": "live_last_12_months",
86387
+ * "idle_days": 90,
86388
+ * "total": {
86389
+ * "count": 111,
86390
+ * "clp": 41000000
86391
+ * },
86392
+ * "focus": "unaccepted",
86393
+ * "rule": "Presupuestos de los últimos 12 meses sin cita futura: «nunca aceptó» o «quedó a medias» tras 90 días sin cita ni pago."
86394
+ * },
86395
+ * "coverage": {
86396
+ * "plans_without_lines": 0,
86397
+ * "excluded_zero": 301,
86398
+ * "excluded_entry_only": 92
86399
+ * }
86400
+ * }
86401
+ * }
86402
+ */
86403
+ "application/json": {
86404
+ data: components["schemas"]["ClinicComercialReport"];
86405
+ };
86406
+ };
86407
+ };
86408
+ /** @description Validation error */
86409
+ 400: {
86410
+ headers: {
86411
+ [name: string]: unknown;
86412
+ };
86413
+ content: {
86414
+ "application/json": components["schemas"]["Error"];
86415
+ };
86416
+ };
86417
+ /** @description Unauthorized */
86418
+ 401: {
86419
+ headers: {
86420
+ [name: string]: unknown;
86421
+ };
86422
+ content: {
86423
+ "application/json": components["schemas"]["Error"];
86424
+ };
86425
+ };
86426
+ /** @description Forbidden */
86427
+ 403: {
86428
+ headers: {
86429
+ [name: string]: unknown;
86430
+ };
86431
+ content: {
86432
+ "application/json": components["schemas"]["Error"];
86433
+ };
86434
+ };
86435
+ /** @description Not found */
86436
+ 404: {
86437
+ headers: {
86438
+ [name: string]: unknown;
86439
+ };
86440
+ content: {
86441
+ "application/json": components["schemas"]["Error"];
86442
+ };
86443
+ };
86444
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
86445
+ 409: {
86446
+ headers: {
86447
+ [name: string]: unknown;
86448
+ };
86449
+ content: {
86450
+ "application/json": components["schemas"]["Error"];
86451
+ };
86452
+ };
86453
+ /** @description Rate limited */
86454
+ 429: {
86455
+ headers: {
86456
+ [name: string]: unknown;
86457
+ };
86458
+ content: {
86459
+ "application/json": components["schemas"]["Error"];
86460
+ };
86461
+ };
86462
+ };
86463
+ };
86464
+ put?: never;
86465
+ post?: never;
86466
+ delete?: never;
86467
+ options?: never;
86468
+ head?: never;
86469
+ patch?: never;
86470
+ trace?: never;
86471
+ };
86472
+ "/clinic/targets/{month}": {
86473
+ parameters: {
86474
+ query?: never;
86475
+ header?: never;
86476
+ path?: never;
86477
+ cookie?: never;
86478
+ };
86479
+ /**
86480
+ * The month's Meta mensual and its progress
86481
+ * @description Requires `clinic_money:read`. The owner's three targets for the month (primeras visitas booked, cierres, cobrado in CLP; `null` = not set) with what has been achieved so far: primeras visitas = citas created in the month whose Tipo de agendamiento is Nuevo or Nuevo · reagenda; cierres as Números comerciales counts them; cobrado = payments received. For the current month, `pace` carries the ritmo diario. The ritmo diario is a catch-up pace: (meta − achieved before today) ÷ working days left, today included. Working days come from the clinic's opening hours and holidays, in the clinic's timezone. A figure with no target has a count and no pace. This is not the Ads goal, which is marketing's own.
86482
+ *
86483
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
86484
+ */
86485
+ get: {
86486
+ parameters: {
86487
+ query?: never;
86488
+ header?: never;
86489
+ path: {
86490
+ month: string;
86491
+ };
86492
+ cookie?: never;
86493
+ };
86494
+ requestBody?: never;
86495
+ responses: {
86496
+ /** @description Targets and progress */
86497
+ 200: {
86498
+ headers: {
86499
+ [name: string]: unknown;
86500
+ };
86501
+ content: {
86502
+ /**
86503
+ * @example {
86504
+ * "data": {
86505
+ * "month": "2026-10",
86506
+ * "timezone": "America/Santiago",
86507
+ * "targets": {
86508
+ * "month": "2026-10",
86509
+ * "primeras_visitas": 40,
86510
+ * "cierres": 12,
86511
+ * "cobrado_clp": 18000000,
86512
+ * "updated_at": "2026-10-01T13:05:00.000Z",
86513
+ * "updated_by": null
86514
+ * },
86515
+ * "achieved": {
86516
+ * "primeras_visitas": 18,
86517
+ * "cierres": 5,
86518
+ * "cobrado_clp": 7450000
86519
+ * },
86520
+ * "working_days": {
86521
+ * "total": 21,
86522
+ * "left": 10
86523
+ * },
86524
+ * "pace": {
86525
+ * "primeras_visitas": {
86526
+ * "status": "open",
86527
+ * "target": 40,
86528
+ * "achieved_before_today": 18,
86529
+ * "remaining": 22,
86530
+ * "working_days_left": 10,
86531
+ * "per_day": 3
86532
+ * },
86533
+ * "cierres": null,
86534
+ * "cobrado_clp": {
86535
+ * "status": "open",
86536
+ * "target": 18000000,
86537
+ * "achieved_before_today": 7450000,
86538
+ * "remaining": 10550000,
86539
+ * "working_days_left": 10,
86540
+ * "per_day": 1055000
86541
+ * }
86542
+ * }
86543
+ * }
86544
+ * }
86545
+ */
86546
+ "application/json": {
86547
+ data: {
86548
+ month: string;
86549
+ timezone: string;
86550
+ targets: {
86551
+ month: string;
86552
+ primeras_visitas: number | null;
86553
+ cierres: number | null;
86554
+ cobrado_clp: number | null;
86555
+ updated_at: string | null;
86556
+ updated_by: string | null;
86557
+ };
86558
+ achieved: {
86559
+ primeras_visitas: number;
86560
+ cierres: number;
86561
+ cobrado_clp: number;
86562
+ };
86563
+ working_days: {
86564
+ total: number;
86565
+ left: number;
86566
+ };
86567
+ pace: {
86568
+ primeras_visitas: {
86569
+ /** @enum {string} */
86570
+ status: "open" | "met" | "missed";
86571
+ target: number;
86572
+ achieved_before_today: number;
86573
+ remaining: number;
86574
+ working_days_left: number;
86575
+ per_day: number | null;
86576
+ } | null;
86577
+ cierres: {
86578
+ /** @enum {string} */
86579
+ status: "open" | "met" | "missed";
86580
+ target: number;
86581
+ achieved_before_today: number;
86582
+ remaining: number;
86583
+ working_days_left: number;
86584
+ per_day: number | null;
86585
+ } | null;
86586
+ cobrado_clp: {
86587
+ /** @enum {string} */
86588
+ status: "open" | "met" | "missed";
86589
+ target: number;
86590
+ achieved_before_today: number;
86591
+ remaining: number;
86592
+ working_days_left: number;
86593
+ per_day: number | null;
86594
+ } | null;
86595
+ } | null;
86596
+ };
86597
+ };
86598
+ };
86599
+ };
86600
+ /** @description Validation error */
86601
+ 400: {
86602
+ headers: {
86603
+ [name: string]: unknown;
86604
+ };
86605
+ content: {
86606
+ "application/json": components["schemas"]["Error"];
86607
+ };
86608
+ };
86609
+ /** @description Unauthorized */
86610
+ 401: {
86611
+ headers: {
86612
+ [name: string]: unknown;
86613
+ };
86614
+ content: {
86615
+ "application/json": components["schemas"]["Error"];
86616
+ };
86617
+ };
86618
+ /** @description Forbidden */
86619
+ 403: {
86620
+ headers: {
86621
+ [name: string]: unknown;
86622
+ };
86623
+ content: {
86624
+ "application/json": components["schemas"]["Error"];
86625
+ };
86626
+ };
86627
+ /** @description Not found */
86628
+ 404: {
86629
+ headers: {
86630
+ [name: string]: unknown;
86631
+ };
86632
+ content: {
86633
+ "application/json": components["schemas"]["Error"];
86634
+ };
86635
+ };
86636
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
86637
+ 409: {
86638
+ headers: {
86639
+ [name: string]: unknown;
86640
+ };
86641
+ content: {
86642
+ "application/json": components["schemas"]["Error"];
86643
+ };
86644
+ };
86645
+ /** @description Rate limited */
86646
+ 429: {
86647
+ headers: {
86648
+ [name: string]: unknown;
86649
+ };
86650
+ content: {
86651
+ "application/json": components["schemas"]["Error"];
86652
+ };
86653
+ };
86654
+ };
86655
+ };
86656
+ /**
86657
+ * Set the month's Meta mensual
86658
+ * @description Requires `clinic_admin:write`. Replaces the month's targets: a figure left out or `null` is cleared. Answers with the same targets and progress as the GET.
86659
+ *
86660
+ * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
86661
+ */
86662
+ put: {
86663
+ parameters: {
86664
+ query?: never;
86665
+ header?: never;
86666
+ path: {
86667
+ month: string;
86668
+ };
86669
+ cookie?: never;
86670
+ };
86671
+ requestBody?: {
86672
+ content: {
86673
+ /**
86674
+ * @example {
86675
+ * "primeras_visitas": 40,
86676
+ * "cierres": 12,
86677
+ * "cobrado_clp": 18000000
86678
+ * }
86679
+ */
86680
+ "application/json": {
86681
+ primeras_visitas?: number | null;
86682
+ cierres?: number | null;
86683
+ cobrado_clp?: number | null;
86684
+ };
86685
+ };
86686
+ };
86687
+ responses: {
86688
+ /** @description Targets and progress */
86689
+ 200: {
86690
+ headers: {
86691
+ [name: string]: unknown;
86692
+ };
86693
+ content: {
86694
+ /**
86695
+ * @example {
86696
+ * "data": {
86697
+ * "month": "2026-10",
86698
+ * "timezone": "America/Santiago",
86699
+ * "targets": {
86700
+ * "month": "2026-10",
86701
+ * "primeras_visitas": 40,
86702
+ * "cierres": 12,
86703
+ * "cobrado_clp": 18000000,
86704
+ * "updated_at": "2026-10-01T13:05:00.000Z",
86705
+ * "updated_by": null
86706
+ * },
86707
+ * "achieved": {
86708
+ * "primeras_visitas": 18,
86709
+ * "cierres": 5,
86710
+ * "cobrado_clp": 7450000
86711
+ * },
86712
+ * "working_days": {
86713
+ * "total": 21,
86714
+ * "left": 10
86715
+ * },
86716
+ * "pace": {
86717
+ * "primeras_visitas": {
86718
+ * "status": "open",
86719
+ * "target": 40,
86720
+ * "achieved_before_today": 18,
86721
+ * "remaining": 22,
86722
+ * "working_days_left": 10,
86723
+ * "per_day": 3
86724
+ * },
86725
+ * "cierres": null,
86726
+ * "cobrado_clp": {
86727
+ * "status": "open",
86728
+ * "target": 18000000,
86729
+ * "achieved_before_today": 7450000,
86730
+ * "remaining": 10550000,
86731
+ * "working_days_left": 10,
86732
+ * "per_day": 1055000
86733
+ * }
86734
+ * }
86735
+ * }
86736
+ * }
86737
+ */
86738
+ "application/json": {
86739
+ data: {
86740
+ month: string;
86741
+ timezone: string;
86742
+ targets: {
86743
+ month: string;
86744
+ primeras_visitas: number | null;
86745
+ cierres: number | null;
86746
+ cobrado_clp: number | null;
86747
+ updated_at: string | null;
86748
+ updated_by: string | null;
86749
+ };
86750
+ achieved: {
86751
+ primeras_visitas: number;
86752
+ cierres: number;
86753
+ cobrado_clp: number;
86754
+ };
86755
+ working_days: {
86756
+ total: number;
86757
+ left: number;
86758
+ };
86759
+ pace: {
86760
+ primeras_visitas: {
86761
+ /** @enum {string} */
86762
+ status: "open" | "met" | "missed";
86763
+ target: number;
86764
+ achieved_before_today: number;
86765
+ remaining: number;
86766
+ working_days_left: number;
86767
+ per_day: number | null;
86768
+ } | null;
86769
+ cierres: {
86770
+ /** @enum {string} */
86771
+ status: "open" | "met" | "missed";
86772
+ target: number;
86773
+ achieved_before_today: number;
86774
+ remaining: number;
86775
+ working_days_left: number;
86776
+ per_day: number | null;
86777
+ } | null;
86778
+ cobrado_clp: {
86779
+ /** @enum {string} */
86780
+ status: "open" | "met" | "missed";
86781
+ target: number;
86782
+ achieved_before_today: number;
86783
+ remaining: number;
86784
+ working_days_left: number;
86785
+ per_day: number | null;
86786
+ } | null;
86787
+ } | null;
86788
+ };
86789
+ };
86790
+ };
86791
+ };
86792
+ /** @description Validation error */
86793
+ 400: {
86794
+ headers: {
86795
+ [name: string]: unknown;
86796
+ };
86797
+ content: {
86798
+ "application/json": components["schemas"]["Error"];
86799
+ };
86800
+ };
86801
+ /** @description Unauthorized */
86802
+ 401: {
86803
+ headers: {
86804
+ [name: string]: unknown;
86805
+ };
86806
+ content: {
86807
+ "application/json": components["schemas"]["Error"];
86808
+ };
86809
+ };
86810
+ /** @description Forbidden */
86811
+ 403: {
86812
+ headers: {
86813
+ [name: string]: unknown;
86814
+ };
86815
+ content: {
86816
+ "application/json": components["schemas"]["Error"];
86817
+ };
86818
+ };
86819
+ /** @description Not found */
86820
+ 404: {
86821
+ headers: {
86822
+ [name: string]: unknown;
86823
+ };
86824
+ content: {
86825
+ "application/json": components["schemas"]["Error"];
86826
+ };
86827
+ };
86828
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
86829
+ 409: {
86830
+ headers: {
86831
+ [name: string]: unknown;
86832
+ };
86833
+ content: {
86834
+ "application/json": components["schemas"]["Error"];
86835
+ };
86836
+ };
86837
+ /** @description Rate limited */
86838
+ 429: {
86839
+ headers: {
86840
+ [name: string]: unknown;
86841
+ };
86842
+ content: {
86843
+ "application/json": components["schemas"]["Error"];
86844
+ };
86845
+ };
86846
+ };
86847
+ };
84578
86848
  post?: never;
84579
86849
  delete?: never;
84580
86850
  options?: never;
@@ -84582,7 +86852,7 @@ interface paths {
84582
86852
  patch?: never;
84583
86853
  trace?: never;
84584
86854
  };
84585
- "/clinic/insights/comercial": {
86855
+ "/clinic/today": {
84586
86856
  parameters: {
84587
86857
  query?: never;
84588
86858
  header?: never;
@@ -84590,30 +86860,21 @@ interface paths {
84590
86860
  cookie?: never;
84591
86861
  };
84592
86862
  /**
84593
- * Commercial numbers: presupuesto acceptance, cierres, cash, botados
84594
- * @description The clinic's commercial numbers, aggregates only (counts, CLP, percentages, months and professional names; never a patient). `regime` says how acceptance is read: `evidencia` when the revenue mirror is on (a non-entry line performed, or a cita booked after the presupuesto, inside its window), `explicita` otherwise (the acceptance the clinic recorded). Acceptance is by COUNT of presupuestos, one per lineage; $0 and entry-only presupuestos are out of both sides; the window is 60 days (90 for ortodoncia and implantología); the rate goes to the professional who issued the presupuesto. Presupuestos ISSUED in the window form the acceptance cohort (`cohorts` by issue month with 30- and 60-day reads). A cierre is the first payment on an accepted presupuesto: once per presupuesto, dated on that payment, valued at the full accepted value. `headline` is the cash collected in the window against the previous window of the same length. `first_visit` (new patients who paid more than the evaluación the day of their first attended cita; `null` with `reason` when no evaluación is priced), `second_visit` (accepted presupuestos captured after the issue day) and `botados` (two lists — «nunca aceptó» and «quedó a medias», 90 days without cita or pago and no future cita, over the presupuestos of the last 12 months, per issuing professional) carry `formula: mentores_2026_09_29`. An acceptance proven by a cita stays accepted when the cita is later cancelled (`cita_only` counts them). `acceptance.reference` and each `by_professional[].reference` place the rate on the mentors' «rango de referencia del programa» (the dentist's especialidad range, else general; `null` when none). Every KPI carries its `formula` and a printable `rule`; a rate with no denominator is `null`, never 0. `state` is `empty`, `settling` (presupuesto lines still being read; `state_reason` says so) or `ready`. Requires `clinic_money:read` (or `clinic_insights:read`, which reads the same aggregates). Accepts `from` / `to` (bare `YYYY-MM-DD`, `to` INCLUSIVE; default the 90 days ending today in the clinic's zone), `location_id` and `professional_id`. With `professional_id`, cash (`headline`, `months[].collected_clp`) is `null`: a payment belongs to the patient, not to a professional. A caller WITHOUT `clinic_money:read` always reads one fixed window — the last 3 closed calendar months — whatever range or filter it asks (`suppression.filters_ignored`), with every count or CLP resting on 1–4 cases `null`, rates over fewer than 5 cases `null`, complementary suppression over professionals, cohorts and botados lists, and month rows carrying only cash.
86863
+ * The «Hoy» strip: is today on track?
86864
+ * @description Requires `clinic:read` or `clinic_money:read`; cobrado figures need `clinic_money:read` and are `null` without it. Bookings made today (citas created today, for any date) with their Tipo de agendamiento split, the bookings of the last hour, the same counts for the previous working day, cobrado today and the part from new patients (those whose cycle began with a primera visita), and the paces of today's month. The ritmo diario is a catch-up pace: (meta − achieved before today) ÷ working days left, today included. Working days come from the clinic's opening hours and holidays, in the clinic's timezone. A figure with no target has a count and no pace. The pace of cobrado is the Meta mensual's, set against the clinic's total cobrado.
84595
86865
  *
84596
86866
  * **Connected apps:** every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, `"M.F. · #1001"` — unless the clinic allowed patient names, with RUT, phone and email masked in free text and `meta.patient_privacy` saying so. Clinical alerts (`flags`) are withheld in both modes, and a non-JSON body (an export, a file) is refused with `403 CONNECTED_APP_SENSITIVE_DATA`.
84597
86867
  */
84598
86868
  get: {
84599
86869
  parameters: {
84600
- query?: {
84601
- /** @description First day of the window, inclusive. Defaults to 90 days before `to`. */
84602
- from?: string;
84603
- /** @description Last day of the window, INCLUSIVE. Defaults to today in the clinic's zone. */
84604
- to?: string;
84605
- /** @description One sucursal. Omit for all of them. */
84606
- location_id?: string;
84607
- /** @description One profesional. A foreign id is a 404, never an empty report. */
84608
- professional_id?: string;
84609
- };
86870
+ query?: never;
84610
86871
  header?: never;
84611
86872
  path?: never;
84612
86873
  cookie?: never;
84613
86874
  };
84614
86875
  requestBody?: never;
84615
86876
  responses: {
84616
- /** @description Clinic commercial numbers */
86877
+ /** @description Today against the ritmo diario */
84617
86878
  200: {
84618
86879
  headers: {
84619
86880
  [name: string]: unknown;
@@ -84622,188 +86883,117 @@ interface paths {
84622
86883
  /**
84623
86884
  * @example {
84624
86885
  * "data": {
84625
- * "from": "2026-06-01",
84626
- * "to": "2026-08-29",
86886
+ * "as_of": "2026-10-13T18:30:00.000Z",
84627
86887
  * "timezone": "America/Santiago",
84628
- * "location_id": null,
84629
- * "professional_id": null,
84630
- * "state": "ready",
84631
- * "state_reason": null,
84632
- * "regime": "evidencia",
84633
- * "regime_label": "Aceptación por evidencia",
84634
- * "regime_rule": "Leído desde tu software de agenda: un presupuesto cuenta como aceptado cuando se realizó una de sus prestaciones o el paciente quedó con una cita agendada después de recibirlo, aunque después la cancele.",
84635
- * "source": {
84636
- * "kind": "mirror",
84637
- * "synced_at": "2026-08-30T03:10:00.000Z"
84638
- * },
84639
- * "formulas": {
84640
- * "acceptance": "mentores_2026_09_25",
84641
- * "cierre": "cierre_2026_09_25",
84642
- * "first_visit": "mentores_2026_09_29",
84643
- * "second_visit": "mentores_2026_09_29",
84644
- * "botados": "mentores_2026_09_29"
84645
- * },
84646
- * "headline": {
84647
- * "collected_clp": 48500000,
84648
- * "previous_collected_clp": 44100000,
84649
- * "delta_pct": 10,
84650
- * "previous_from": "2026-03-03",
84651
- * "previous_to": "2026-05-31",
84652
- * "rule": "Lo cobrado en el período: todos los pagos recibidos, sin los anulados. Se compara con el período anterior del mismo largo."
84653
- * },
84654
- * "acceptance": {
84655
- * "presented": 420,
84656
- * "accepted": 231,
84657
- * "rate": 55,
84658
- * "same_day": 118,
84659
- * "later": 113,
84660
- * "cita_only": 14,
84661
- * "pending": 36,
84662
- * "presented_clp": 142000000,
84663
- * "accepted_clp": 71300000,
84664
- * "rate_30d": 47.2,
84665
- * "rate_60d": 53.8,
84666
- * "formula": "mentores_2026_09_25",
84667
- * "window_days": 60,
84668
- * "long_window_days": 90,
84669
- * "rule": "Presupuestos aceptados ÷ presupuestos presentados, cada presupuesto una vez.",
84670
- * "reference": {
84671
- * "label": "Rango de referencia del programa",
84672
- * "especialidad": null,
84673
- * "band": "malo",
84674
- * "note": null,
84675
- * "muy_malo_upper_pct": 40,
84676
- * "malo_upper_pct": 60,
84677
- * "regular_upper_pct": 70,
84678
- * "excelente_lower_pct": 90
84679
- * }
84680
- * },
84681
- * "cierres": {
84682
- * "count": 180,
84683
- * "value_clp": 58900000,
84684
- * "accepted_without_payment": 51,
84685
- * "formula": "cierre_2026_09_25",
84686
- * "rule": "Un cierre es el primer pago de un presupuesto aceptado."
84687
- * },
84688
- * "cohorts": [
84689
- * {
84690
- * "month": "2026-06",
84691
- * "presented": 140,
84692
- * "accepted": 80,
84693
- * "rate": 57.1,
84694
- * "same_day": 41,
84695
- * "later": 39,
84696
- * "cita_only": 2,
84697
- * "pending": 0,
84698
- * "presented_clp": 47000000,
84699
- * "accepted_clp": 24800000,
84700
- * "rate_30d": 49.3,
84701
- * "rate_60d": 56.4,
84702
- * "mature": true
84703
- * }
84704
- * ],
84705
- * "by_professional": [
84706
- * {
84707
- * "professional_id": "0f3c2a9e-5b1d-4c7e-9a42-6d8e1f2b3c4d",
84708
- * "professional_name": "Dra. Camila Soto",
84709
- * "presented": 96,
84710
- * "accepted": 61,
84711
- * "rate": 63.5,
84712
- * "same_day": 30,
84713
- * "later": 31,
84714
- * "cita_only": 3,
84715
- * "pending": 8,
84716
- * "presented_clp": 33400000,
84717
- * "accepted_clp": 19900000,
84718
- * "cierres": 47,
84719
- * "cierres_clp": 15100000,
84720
- * "reference": {
84721
- * "label": "Rango de referencia del programa",
84722
- * "especialidad": null,
84723
- * "band": "regular",
84724
- * "note": null,
84725
- * "muy_malo_upper_pct": 40,
84726
- * "malo_upper_pct": 60,
84727
- * "regular_upper_pct": 70,
84728
- * "excelente_lower_pct": 90
86888
+ * "today": {
86889
+ * "date": "2026-10-13",
86890
+ * "is_working_day": true,
86891
+ * "booked": {
86892
+ * "total": 9,
86893
+ * "primeras_visitas": 4,
86894
+ * "by_kind": {
86895
+ * "new": 3,
86896
+ * "new_rebooked": 1,
86897
+ * "in_treatment": 5
84729
86898
  * }
84730
- * }
84731
- * ],
84732
- * "unassigned_presented": 0,
84733
- * "unassigned_cierres": 0,
84734
- * "months": [
84735
- * {
84736
- * "month": "2026-06",
84737
- * "accepted_clp": 24100000,
84738
- * "produced_clp": 19700000,
84739
- * "collected_clp": 16200000,
84740
- * "cierres": 62,
84741
- * "cierres_clp": 20300000
84742
- * }
84743
- * ],
84744
- * "first_visit": {
84745
- * "formula": "mentores_2026_09_29",
84746
- * "patients": 210,
84747
- * "paid": 97,
84748
- * "rate": 46.2,
84749
- * "evaluation_price_clp": 34990,
84750
- * "reason": null,
84751
- * "rule": "De los pacientes nuevos cuya primera cita atendida cae en el período, la parte que ese día pagó más que la evaluación ($34.990)."
84752
- * },
84753
- * "second_visit": {
84754
- * "formula": "mentores_2026_09_29",
84755
- * "eligible": 231,
84756
- * "converted": 113,
84757
- * "rate": 48.9,
84758
- * "rule": "De los presupuestos aceptados del período, la parte que se aceptó en una visita posterior y no el mismo día en que se entregó."
84759
- * },
84760
- * "botados": {
84761
- * "unaccepted": {
84762
- * "count": 88,
84763
- * "clp": 31200000
84764
- * },
84765
- * "accepted_unscheduled": {
84766
- * "count": 0,
84767
- * "clp": 0
84768
86899
  * },
84769
- * "mid_treatment_no_future_cita": {
84770
- * "count": 23,
84771
- * "clp": 9800000
86900
+ * "last_hour": {
86901
+ * "total": 2,
86902
+ * "primeras_visitas": 1
84772
86903
  * },
84773
- * "by_professional": [
84774
- * {
84775
- * "professional_id": "0f3c2a9e-5b1d-4c7e-9a42-6d8e1f2b3c4d",
84776
- * "professional_name": "Dra. Camila Soto",
84777
- * "unaccepted": {
84778
- * "count": 21,
84779
- * "clp": 7400000
84780
- * },
84781
- * "mid_treatment_no_future_cita": {
84782
- * "count": 6,
84783
- * "clp": 2900000
84784
- * }
86904
+ * "cobrado_clp": 640000,
86905
+ * "cobrado_new_patients_clp": 90000
86906
+ * },
86907
+ * "previous_working_day": {
86908
+ * "date": "2026-10-09",
86909
+ * "booked": {
86910
+ * "total": 11,
86911
+ * "primeras_visitas": 3,
86912
+ * "by_kind": {
86913
+ * "new": 2,
86914
+ * "new_rebooked": 1,
86915
+ * "in_treatment": 8
84785
86916
  * }
84786
- * ],
84787
- * "formula": "mentores_2026_09_29",
84788
- * "basis": "live_last_12_months",
84789
- * "idle_days": 90,
84790
- * "total": {
84791
- * "count": 111,
84792
- * "clp": 41000000
84793
86917
  * },
84794
- * "focus": "unaccepted",
84795
- * "rule": "Presupuestos de los últimos 12 meses sin cita futura: «nunca aceptó» o «quedó a medias» tras 90 días sin cita ni pago."
86918
+ * "cobrado_clp": 810000,
86919
+ * "cobrado_new_patients_clp": 120000
84796
86920
  * },
84797
- * "coverage": {
84798
- * "plans_without_lines": 0,
84799
- * "excluded_zero": 301,
84800
- * "excluded_entry_only": 92
86921
+ * "pace": {
86922
+ * "month": "2026-10",
86923
+ * "working_days_left": 14,
86924
+ * "primeras_visitas": {
86925
+ * "status": "open",
86926
+ * "target": 40,
86927
+ * "achieved_before_today": 18,
86928
+ * "remaining": 22,
86929
+ * "working_days_left": 10,
86930
+ * "per_day": 3
86931
+ * },
86932
+ * "cobrado_clp": null
84801
86933
  * }
84802
86934
  * }
84803
86935
  * }
84804
86936
  */
84805
86937
  "application/json": {
84806
- data: components["schemas"]["ClinicComercialReport"];
86938
+ data: {
86939
+ as_of: string;
86940
+ timezone: string;
86941
+ today: {
86942
+ date: string;
86943
+ booked: {
86944
+ total: number;
86945
+ primeras_visitas: number;
86946
+ by_kind: {
86947
+ new: number;
86948
+ new_rebooked: number;
86949
+ in_treatment: number;
86950
+ };
86951
+ };
86952
+ cobrado_clp: number | null;
86953
+ cobrado_new_patients_clp: number | null;
86954
+ is_working_day: boolean;
86955
+ last_hour: {
86956
+ total: number;
86957
+ primeras_visitas: number;
86958
+ };
86959
+ };
86960
+ previous_working_day: {
86961
+ date: string;
86962
+ booked: {
86963
+ total: number;
86964
+ primeras_visitas: number;
86965
+ by_kind: {
86966
+ new: number;
86967
+ new_rebooked: number;
86968
+ in_treatment: number;
86969
+ };
86970
+ };
86971
+ cobrado_clp: number | null;
86972
+ cobrado_new_patients_clp: number | null;
86973
+ } | null;
86974
+ pace: {
86975
+ month: string;
86976
+ working_days_left: number;
86977
+ primeras_visitas: {
86978
+ /** @enum {string} */
86979
+ status: "open" | "met" | "missed";
86980
+ target: number;
86981
+ achieved_before_today: number;
86982
+ remaining: number;
86983
+ working_days_left: number;
86984
+ per_day: number | null;
86985
+ } | null;
86986
+ cobrado_clp: {
86987
+ /** @enum {string} */
86988
+ status: "open" | "met" | "missed";
86989
+ target: number;
86990
+ achieved_before_today: number;
86991
+ remaining: number;
86992
+ working_days_left: number;
86993
+ per_day: number | null;
86994
+ } | null;
86995
+ };
86996
+ };
84807
86997
  };
84808
86998
  };
84809
86999
  };
@@ -125848,11 +128038,13 @@ interface paths {
125848
128038
  put?: never;
125849
128039
  /**
125850
128040
  * Upload a knowledge file
125851
- * @description A **`multipart/form-data`** request with the document in a field named `file` — not a JSON body. The filename and content type come from the part itself. Capped at 25 MB; an empty file and a missing part are both 400s.
128041
+ * @description A **`multipart/form-data`** request with the document in a field named `file`. The filename and content type come from the part itself. Capped at 25 MB (413 above it); an empty file and a missing part are both 400s.
128042
+ *
128043
+ * **Or a JSON body** (`application/json`): `{ filename, content, content_encoding?, content_type? }`, with `content` as UTF-8 text by default or base64 when `content_encoding` is `"base64"` (PDF, DOCX, images). The 25 MB cap applies to the decoded bytes; invalid base64 is a 400. The content type defaults from the filename extension. This is the shape an MCP connector sends through `call_operation`, which cannot build multipart. The JSON body may also carry `attach_to_agent_id` to attach the file to that agent in the same call, which needs `ai_agents:write` as well.
125852
128044
  *
125853
128045
  * Storage and the database row are written together — if the row fails the stored object is removed, so there are no orphans. Ingestion is then enqueued **best-effort**: a 201 means the file is stored, not that it is searchable. Poll `status` on the list, and use `/{id}/reingest` if it never leaves `ready`.
125854
128046
  *
125855
- * Uploading here puts the file in the library and attaches it to NO agent. To upload straight onto an agent, post to `/ai-agents/{id}/knowledge` instead. Answers 201.
128047
+ * Uploading here puts the file in the library and attaches it to NO agent (unless the JSON body names `attach_to_agent_id`). To upload straight onto an agent with multipart, post to `/ai-agents/{id}/knowledge` instead. Answers 201.
125856
128048
  */
125857
128049
  post: {
125858
128050
  parameters: {
@@ -125878,6 +128070,31 @@ interface paths {
125878
128070
  */
125879
128071
  file: string;
125880
128072
  };
128073
+ /**
128074
+ * @example {
128075
+ * "filename": "precios.md",
128076
+ * "content": "# Precios 2026\n\nLimpieza dental: $35.000",
128077
+ * "content_encoding": "utf8"
128078
+ * }
128079
+ */
128080
+ "application/json": {
128081
+ /** @description File name with extension. The parser picks md/txt/csv/json/pdf/docx by it. */
128082
+ filename: string;
128083
+ /** @description The document. UTF-8 text by default; base64 when `content_encoding` is "base64". At most 25 MB once decoded. */
128084
+ content: string;
128085
+ /**
128086
+ * @description `utf8` (default) for text, `base64` for PDF / DOCX / images.
128087
+ * @enum {string}
128088
+ */
128089
+ content_encoding?: "utf8" | "base64";
128090
+ /** @description MIME type. Defaults from the extension: .md → text/markdown, .pdf → application/pdf, … */
128091
+ content_type?: string;
128092
+ /**
128093
+ * Format: uuid
128094
+ * @description Attach to this agent in the same call. Needs `ai_agents:write` as well; the agent must belong to your workspace.
128095
+ */
128096
+ attach_to_agent_id?: string;
128097
+ };
125881
128098
  };
125882
128099
  };
125883
128100
  responses: {
@@ -126177,6 +128394,8 @@ interface paths {
126177
128394
  * The name, content type and size become those of the new part. Its previously indexed chunks are purged **in the same transaction** as the swap, so the agent never quotes the superseded text while the new ingestion runs; the row answers with `status: ingesting` and settles to `ingested` (or `failed`) like an upload. Same 25 MB cap. A deleted or foreign file is a 404.
126178
128395
  *
126179
128396
  * **Optimistic concurrency.** Every replace bumps the file `version` (on the row, and in the `X-Config-Version` / `ETag` headers). Send the version you read as a form field `expected_version` (or `If-Match`): if someone uploaded a newer version since, the replace answers **409** `VERSION_CONFLICT` and nothing is stored. Omitting it still works (logged). Every replace leaves a `kb_files.replace` audit row naming the new and previous version, through REST and MCP alike.
128397
+ *
128398
+ * **Or a JSON body** (`application/json`): `{ filename, content, content_encoding?, content_type? }`, with `content` as UTF-8 text by default or base64 when `content_encoding` is `"base64"` (PDF, DOCX, images). The 25 MB cap applies to the decoded bytes; invalid base64 is a 400. The content type defaults from the filename extension. This is the shape an MCP connector sends through `call_operation`, which cannot build multipart. `expected_version` rides in the same JSON body.
126180
128399
  */
126181
128400
  put: {
126182
128401
  parameters: {
@@ -126203,6 +128422,29 @@ interface paths {
126203
128422
  /** @description The file `version` you last read. Stale → 409 `VERSION_CONFLICT`, nothing stored. Optional (backward compatible); `If-Match` is accepted instead. */
126204
128423
  expected_version?: number;
126205
128424
  };
128425
+ /**
128426
+ * @example {
128427
+ * "filename": "precios.pdf",
128428
+ * "content": "JVBERi0xLjQK…",
128429
+ * "content_encoding": "base64",
128430
+ * "expected_version": 3
128431
+ * }
128432
+ */
128433
+ "application/json": {
128434
+ /** @description File name with extension. The parser picks md/txt/csv/json/pdf/docx by it. */
128435
+ filename: string;
128436
+ /** @description The document. UTF-8 text by default; base64 when `content_encoding` is "base64". At most 25 MB once decoded. */
128437
+ content: string;
128438
+ /**
128439
+ * @description `utf8` (default) for text, `base64` for PDF / DOCX / images.
128440
+ * @enum {string}
128441
+ */
128442
+ content_encoding?: "utf8" | "base64";
128443
+ /** @description MIME type. Defaults from the extension: .md → text/markdown, .pdf → application/pdf, … */
128444
+ content_type?: string;
128445
+ /** @description The file `version` you last read. Stale → 409 `VERSION_CONFLICT`, nothing stored. Optional; `If-Match` is accepted instead. */
128446
+ expected_version?: number;
128447
+ };
126206
128448
  };
126207
128449
  };
126208
128450
  responses: {
@@ -146960,7 +149202,7 @@ interface components {
146960
149202
  /** @enum {string} */
146961
149203
  kind: "record";
146962
149204
  /** @enum {string} */
146963
- record: "conversation" | "contact" | "lead" | "busqueda" | "appointment" | "ai_agent" | "price_approval" | "bank_movement" | "vehicle" | "whatsapp_flow";
149205
+ record: "conversation" | "contact" | "lead" | "busqueda" | "appointment" | "ai_agent" | "price_approval" | "bank_movement" | "vehicle" | "whatsapp_flow" | "campaign";
146964
149206
  /** Format: uuid */
146965
149207
  id: string;
146966
149208
  tab?: string;
@@ -147002,7 +149244,7 @@ interface components {
147002
149244
  /** @enum {string} */
147003
149245
  kind: "record";
147004
149246
  /** @enum {string} */
147005
- record: "conversation" | "contact" | "lead" | "busqueda" | "appointment" | "ai_agent" | "price_approval" | "bank_movement" | "vehicle" | "whatsapp_flow";
149247
+ record: "conversation" | "contact" | "lead" | "busqueda" | "appointment" | "ai_agent" | "price_approval" | "bank_movement" | "vehicle" | "whatsapp_flow" | "campaign";
147006
149248
  /** Format: uuid */
147007
149249
  id: string;
147008
149250
  tab?: string;
@@ -147029,7 +149271,7 @@ interface components {
147029
149271
  /** @enum {string} */
147030
149272
  kind: "record";
147031
149273
  /** @enum {string} */
147032
- record: "conversation" | "contact" | "lead" | "busqueda" | "appointment" | "ai_agent" | "price_approval" | "bank_movement" | "vehicle" | "whatsapp_flow";
149274
+ record: "conversation" | "contact" | "lead" | "busqueda" | "appointment" | "ai_agent" | "price_approval" | "bank_movement" | "vehicle" | "whatsapp_flow" | "campaign";
147033
149275
  /** Format: uuid */
147034
149276
  id: string;
147035
149277
  tab?: string;
@@ -148383,6 +150625,53 @@ interface components {
148383
150625
  min_count: number;
148384
150626
  } | null;
148385
150627
  };
150628
+ ClinicLinea: {
150629
+ /** Format: uuid */
150630
+ id: string;
150631
+ nombre: string;
150632
+ is_default: boolean;
150633
+ archived: boolean;
150634
+ leads: number;
150635
+ open_leads: number;
150636
+ rules: {
150637
+ /** Format: uuid */
150638
+ id: string;
150639
+ match: string;
150640
+ /** @enum {string} */
150641
+ source: "seed" | "owner";
150642
+ }[];
150643
+ specialties: {
150644
+ /** Format: uuid */
150645
+ id: string;
150646
+ nombre: string;
150647
+ }[];
150648
+ };
150649
+ ClinicLineasOverview: {
150650
+ lineas: components["schemas"]["ClinicLinea"][];
150651
+ /** @description Roster specialties no Línea holds yet, for the owner to place. */
150652
+ unmapped_specialties: {
150653
+ /** Format: uuid */
150654
+ id: string;
150655
+ nombre: string;
150656
+ }[];
150657
+ /** @description Every ad that opened a conversation, with its Línea. */
150658
+ ads: {
150659
+ external_id: string;
150660
+ campaign_name: string | null;
150661
+ ad_set_name: string | null;
150662
+ ad_name: string | null;
150663
+ /**
150664
+ * Format: uuid
150665
+ * @description `null` = unmapped, listed for the owner to place.
150666
+ */
150667
+ linea_id: string | null;
150668
+ /** @enum {string|null} */
150669
+ via: "rule" | "specialty_map" | "specialty_name" | null;
150670
+ matched: string | null;
150671
+ conversations: number;
150672
+ leads: number;
150673
+ }[];
150674
+ };
148386
150675
  PaymentRegistration: {
148387
150676
  /** Format: uuid */
148388
150677
  id: string;