@zavudev/sdk 0.58.1 → 0.59.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 (220) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +18 -0
  3. package/client.d.mts +2 -0
  4. package/client.d.mts.map +1 -1
  5. package/client.d.ts +2 -0
  6. package/client.d.ts.map +1 -1
  7. package/client.js +2 -0
  8. package/client.js.map +1 -1
  9. package/client.mjs +2 -0
  10. package/client.mjs.map +1 -1
  11. package/package.json +21 -1
  12. package/src/client.ts +2 -0
  13. package/src/types.ts +2 -2
  14. package/src/v2/index.ts +4 -0
  15. package/src/v2/resources/agents/agents.ts +142 -0
  16. package/src/v2/resources/agents/channels.ts +66 -0
  17. package/src/v2/resources/agents/index.ts +18 -0
  18. package/src/v2/resources/agents.ts +3 -0
  19. package/src/v2/resources/campaigns/campaigns.ts +178 -0
  20. package/src/v2/resources/campaigns/index.ts +27 -0
  21. package/src/v2/resources/campaigns/recipients.ts +62 -0
  22. package/src/v2/resources/campaigns.ts +3 -0
  23. package/src/v2/resources/channels.ts +156 -0
  24. package/src/v2/resources/contacts.ts +112 -0
  25. package/src/v2/resources/conversations.ts +89 -0
  26. package/src/v2/resources/email-domains.ts +107 -0
  27. package/src/v2/resources/functions.ts +73 -0
  28. package/src/v2/resources/index.ts +138 -0
  29. package/src/v2/resources/messages.ts +129 -0
  30. package/src/v2/resources/numbers.ts +74 -0
  31. package/src/v2/resources/templates.ts +104 -0
  32. package/src/v2/resources/webhook.ts +102 -0
  33. package/src/v2/types.ts +2412 -0
  34. package/src/v2/v2.ts +315 -0
  35. package/src/v2.ts +3 -0
  36. package/src/version.ts +1 -1
  37. package/types.d.mts +2 -2
  38. package/types.d.mts.map +1 -1
  39. package/types.d.ts +2 -2
  40. package/types.d.ts.map +1 -1
  41. package/v2/index.d.mts +3 -0
  42. package/v2/index.d.mts.map +1 -0
  43. package/v2/index.d.ts +3 -0
  44. package/v2/index.d.ts.map +1 -0
  45. package/v2/index.js +9 -0
  46. package/v2/index.js.map +1 -0
  47. package/v2/index.mjs +4 -0
  48. package/v2/index.mjs.map +1 -0
  49. package/v2/resources/agents/agents.d.mts +81 -0
  50. package/v2/resources/agents/agents.d.mts.map +1 -0
  51. package/v2/resources/agents/agents.d.ts +81 -0
  52. package/v2/resources/agents/agents.d.ts.map +1 -0
  53. package/v2/resources/agents/agents.js +88 -0
  54. package/v2/resources/agents/agents.js.map +1 -0
  55. package/v2/resources/agents/agents.mjs +83 -0
  56. package/v2/resources/agents/agents.mjs.map +1 -0
  57. package/v2/resources/agents/channels.d.mts +39 -0
  58. package/v2/resources/agents/channels.d.mts.map +1 -0
  59. package/v2/resources/agents/channels.d.ts +39 -0
  60. package/v2/resources/agents/channels.d.ts.map +1 -0
  61. package/v2/resources/agents/channels.js +44 -0
  62. package/v2/resources/agents/channels.js.map +1 -0
  63. package/v2/resources/agents/channels.mjs +40 -0
  64. package/v2/resources/agents/channels.mjs.map +1 -0
  65. package/v2/resources/agents/index.d.mts +3 -0
  66. package/v2/resources/agents/index.d.mts.map +1 -0
  67. package/v2/resources/agents/index.d.ts +3 -0
  68. package/v2/resources/agents/index.d.ts.map +1 -0
  69. package/v2/resources/agents/index.js +9 -0
  70. package/v2/resources/agents/index.js.map +1 -0
  71. package/v2/resources/agents/index.mjs +4 -0
  72. package/v2/resources/agents/index.mjs.map +1 -0
  73. package/v2/resources/agents.d.mts +2 -0
  74. package/v2/resources/agents.d.mts.map +1 -0
  75. package/v2/resources/agents.d.ts +2 -0
  76. package/v2/resources/agents.d.ts.map +1 -0
  77. package/v2/resources/agents.js +6 -0
  78. package/v2/resources/agents.js.map +1 -0
  79. package/v2/resources/agents.mjs +3 -0
  80. package/v2/resources/agents.mjs.map +1 -0
  81. package/v2/resources/campaigns/campaigns.d.mts +100 -0
  82. package/v2/resources/campaigns/campaigns.d.mts.map +1 -0
  83. package/v2/resources/campaigns/campaigns.d.ts +100 -0
  84. package/v2/resources/campaigns/campaigns.d.ts.map +1 -0
  85. package/v2/resources/campaigns/campaigns.js +106 -0
  86. package/v2/resources/campaigns/campaigns.js.map +1 -0
  87. package/v2/resources/campaigns/campaigns.mjs +101 -0
  88. package/v2/resources/campaigns/campaigns.mjs.map +1 -0
  89. package/v2/resources/campaigns/index.d.mts +3 -0
  90. package/v2/resources/campaigns/index.d.mts.map +1 -0
  91. package/v2/resources/campaigns/index.d.ts +3 -0
  92. package/v2/resources/campaigns/index.d.ts.map +1 -0
  93. package/v2/resources/campaigns/index.js +9 -0
  94. package/v2/resources/campaigns/index.js.map +1 -0
  95. package/v2/resources/campaigns/index.mjs +4 -0
  96. package/v2/resources/campaigns/index.mjs.map +1 -0
  97. package/v2/resources/campaigns/recipients.d.mts +43 -0
  98. package/v2/resources/campaigns/recipients.d.mts.map +1 -0
  99. package/v2/resources/campaigns/recipients.d.ts +43 -0
  100. package/v2/resources/campaigns/recipients.d.ts.map +1 -0
  101. package/v2/resources/campaigns/recipients.js +41 -0
  102. package/v2/resources/campaigns/recipients.js.map +1 -0
  103. package/v2/resources/campaigns/recipients.mjs +37 -0
  104. package/v2/resources/campaigns/recipients.mjs.map +1 -0
  105. package/v2/resources/campaigns.d.mts +2 -0
  106. package/v2/resources/campaigns.d.mts.map +1 -0
  107. package/v2/resources/campaigns.d.ts +2 -0
  108. package/v2/resources/campaigns.d.ts.map +1 -0
  109. package/v2/resources/campaigns.js +6 -0
  110. package/v2/resources/campaigns.js.map +1 -0
  111. package/v2/resources/campaigns.mjs +3 -0
  112. package/v2/resources/campaigns.mjs.map +1 -0
  113. package/v2/resources/channels.d.mts +101 -0
  114. package/v2/resources/channels.d.mts.map +1 -0
  115. package/v2/resources/channels.d.ts +101 -0
  116. package/v2/resources/channels.d.ts.map +1 -0
  117. package/v2/resources/channels.js +107 -0
  118. package/v2/resources/channels.js.map +1 -0
  119. package/v2/resources/channels.mjs +103 -0
  120. package/v2/resources/channels.mjs.map +1 -0
  121. package/v2/resources/contacts.d.mts +62 -0
  122. package/v2/resources/contacts.d.mts.map +1 -0
  123. package/v2/resources/contacts.d.ts +62 -0
  124. package/v2/resources/contacts.d.ts.map +1 -0
  125. package/v2/resources/contacts.js +66 -0
  126. package/v2/resources/contacts.js.map +1 -0
  127. package/v2/resources/contacts.mjs +62 -0
  128. package/v2/resources/contacts.mjs.map +1 -0
  129. package/v2/resources/conversations.d.mts +51 -0
  130. package/v2/resources/conversations.d.mts.map +1 -0
  131. package/v2/resources/conversations.d.ts +51 -0
  132. package/v2/resources/conversations.d.ts.map +1 -0
  133. package/v2/resources/conversations.js +55 -0
  134. package/v2/resources/conversations.js.map +1 -0
  135. package/v2/resources/conversations.mjs +51 -0
  136. package/v2/resources/conversations.mjs.map +1 -0
  137. package/v2/resources/email-domains.d.mts +72 -0
  138. package/v2/resources/email-domains.d.mts.map +1 -0
  139. package/v2/resources/email-domains.d.ts +72 -0
  140. package/v2/resources/email-domains.d.ts.map +1 -0
  141. package/v2/resources/email-domains.js +80 -0
  142. package/v2/resources/email-domains.js.map +1 -0
  143. package/v2/resources/email-domains.mjs +76 -0
  144. package/v2/resources/email-domains.mjs.map +1 -0
  145. package/v2/resources/functions.d.mts +41 -0
  146. package/v2/resources/functions.d.mts.map +1 -0
  147. package/v2/resources/functions.d.ts +41 -0
  148. package/v2/resources/functions.d.ts.map +1 -0
  149. package/v2/resources/functions.js +41 -0
  150. package/v2/resources/functions.js.map +1 -0
  151. package/v2/resources/functions.mjs +37 -0
  152. package/v2/resources/functions.mjs.map +1 -0
  153. package/v2/resources/index.d.mts +12 -0
  154. package/v2/resources/index.d.mts.map +1 -0
  155. package/v2/resources/index.d.ts +12 -0
  156. package/v2/resources/index.d.ts.map +1 -0
  157. package/v2/resources/index.js +27 -0
  158. package/v2/resources/index.js.map +1 -0
  159. package/v2/resources/index.mjs +13 -0
  160. package/v2/resources/index.mjs.map +1 -0
  161. package/v2/resources/messages.d.mts +82 -0
  162. package/v2/resources/messages.d.mts.map +1 -0
  163. package/v2/resources/messages.d.ts +82 -0
  164. package/v2/resources/messages.d.ts.map +1 -0
  165. package/v2/resources/messages.js +74 -0
  166. package/v2/resources/messages.js.map +1 -0
  167. package/v2/resources/messages.mjs +70 -0
  168. package/v2/resources/messages.mjs.map +1 -0
  169. package/v2/resources/numbers.d.mts +46 -0
  170. package/v2/resources/numbers.d.mts.map +1 -0
  171. package/v2/resources/numbers.d.ts +46 -0
  172. package/v2/resources/numbers.d.ts.map +1 -0
  173. package/v2/resources/numbers.js +41 -0
  174. package/v2/resources/numbers.js.map +1 -0
  175. package/v2/resources/numbers.mjs +37 -0
  176. package/v2/resources/numbers.mjs.map +1 -0
  177. package/v2/resources/templates.d.mts +56 -0
  178. package/v2/resources/templates.d.mts.map +1 -0
  179. package/v2/resources/templates.d.ts +56 -0
  180. package/v2/resources/templates.d.ts.map +1 -0
  181. package/v2/resources/templates.js +61 -0
  182. package/v2/resources/templates.js.map +1 -0
  183. package/v2/resources/templates.mjs +57 -0
  184. package/v2/resources/templates.mjs.map +1 -0
  185. package/v2/resources/webhook.d.mts +67 -0
  186. package/v2/resources/webhook.d.mts.map +1 -0
  187. package/v2/resources/webhook.d.ts +67 -0
  188. package/v2/resources/webhook.d.ts.map +1 -0
  189. package/v2/resources/webhook.js +70 -0
  190. package/v2/resources/webhook.js.map +1 -0
  191. package/v2/resources/webhook.mjs +66 -0
  192. package/v2/resources/webhook.mjs.map +1 -0
  193. package/v2/types.d.mts +2171 -0
  194. package/v2/types.d.mts.map +1 -0
  195. package/v2/types.d.ts +2171 -0
  196. package/v2/types.d.ts.map +1 -0
  197. package/v2/types.js +4 -0
  198. package/v2/types.js.map +1 -0
  199. package/v2/types.mjs +3 -0
  200. package/v2/types.mjs.map +1 -0
  201. package/v2/v2.d.mts +40 -0
  202. package/v2/v2.d.mts.map +1 -0
  203. package/v2/v2.d.ts +40 -0
  204. package/v2/v2.d.ts.map +1 -0
  205. package/v2/v2.js +47 -0
  206. package/v2/v2.js.map +1 -0
  207. package/v2/v2.mjs +42 -0
  208. package/v2/v2.mjs.map +1 -0
  209. package/v2.d.mts +2 -0
  210. package/v2.d.mts.map +1 -0
  211. package/v2.d.ts +2 -0
  212. package/v2.d.ts.map +1 -0
  213. package/v2.js +6 -0
  214. package/v2.js.map +1 -0
  215. package/v2.mjs +3 -0
  216. package/v2.mjs.map +1 -0
  217. package/version.d.mts +1 -1
  218. package/version.d.ts +1 -1
  219. package/version.js +1 -1
  220. package/version.mjs +1 -1
@@ -0,0 +1,2412 @@
1
+ // Generated by zavu-codegen from apps/docs/api-reference/v2/openapi.json. Do not edit: run `bun run codegen`.
2
+
3
+ // The types every resource module aliases under its public names. Not part of the
4
+ // package's export map: import the names from `@zavudev/sdk` or its resource modules.
5
+
6
+ /**
7
+ * A channel whose connection has an on/off switch. `sms`, `sms_oneway` and `voice`
8
+ * are billed per message and per phone number, are always on, and have nothing to
9
+ * activate.
10
+ */
11
+ export type ActivatableChannel = 'whatsapp' | 'telegram' | 'instagram' | 'messenger' | 'email';
12
+
13
+ /**
14
+ * Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came
15
+ * from.
16
+ *
17
+ * WhatsApp only. Present on the **first inbound message** of a conversation opened
18
+ * from a Meta ad or post, and on no message after it — so store it when it
19
+ * arrives rather than expecting it again. Organic conversations never carry it.
20
+ *
21
+ * Field names are camelCased to match the rest of this API; Meta sends them as
22
+ * snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are
23
+ * omitted: a `post` source has no click id, and an image ad has no `videoUrl`.
24
+ */
25
+ export interface AdReferral {
26
+ /** Where the click came from. */
27
+ sourceType?: AdReferralSourceType;
28
+ /** Identifier of the ad or post that produced the click. */
29
+ sourceId?: string;
30
+ /** Meta permalink to the ad or post. */
31
+ sourceUrl?: string;
32
+ /** Headline of the ad or post. */
33
+ headline?: string;
34
+ /** Body copy of the ad or post. */
35
+ body?: string;
36
+ /** Type of media on the ad, when it had any. */
37
+ mediaType?: AdReferralMediaType;
38
+ /** Image of the ad. Present when `mediaType` is `image`. */
39
+ imageUrl?: string;
40
+ /** Video of the ad. Present when `mediaType` is `video`. */
41
+ videoUrl?: string;
42
+ /** Thumbnail of the ad media. */
43
+ thumbnailUrl?: string;
44
+ /**
45
+ * Click-to-WhatsApp click identifier. This is the value Meta's Conversions API
46
+ * needs to credit a conversion back to the ad that produced the conversation.
47
+ * Present on `ad` sources; a `post` source has none.
48
+ */
49
+ ctwaClid?: string;
50
+ }
51
+
52
+ export type AdReferralSourceType = 'ad' | 'post';
53
+
54
+ export type AdReferralMediaType = 'image' | 'video';
55
+
56
+ /** An AI agent. It answers on the addresses in `channels`. */
57
+ export interface Agent {
58
+ id: string;
59
+ name: string;
60
+ /** Whether the agent is active. */
61
+ enabled: boolean;
62
+ provider: AgentProvider;
63
+ /** Model ID (e.g., gpt-4o-mini, claude-3-5-sonnet). */
64
+ model: string;
65
+ /** System prompt for the agent. */
66
+ systemPrompt: string;
67
+ /**
68
+ * Number of previous messages to include as context.
69
+ *
70
+ * @defaultValue `10`
71
+ */
72
+ contextWindowMessages?: number;
73
+ /**
74
+ * Whether to include contact metadata in context.
75
+ *
76
+ * @defaultValue `true`
77
+ */
78
+ includeContactMetadata?: boolean;
79
+ /** Maximum tokens for LLM response. */
80
+ maxTokens?: number | null;
81
+ /** LLM temperature (0-2). */
82
+ temperature?: number | null;
83
+ /** Channels that trigger the agent. */
84
+ triggerOnChannels?: Array<string>;
85
+ /** Message types that trigger the agent. */
86
+ triggerOnMessageTypes?: Array<string>;
87
+ stats?: AgentStats;
88
+ createdAt: string;
89
+ updatedAt: string;
90
+ /**
91
+ * Voice Agent configuration. When present and enabled, the agent can answer
92
+ * inbound phone calls and place outbound calls with Zavu's managed voice pipeline.
93
+ * Requires the Voice Agents feature to be enabled for your team.
94
+ */
95
+ voice?: AgentVoiceConfig;
96
+ /**
97
+ * The (address, type) entries this agent answers on: every transport of the
98
+ * addresses it is connected to that its `triggerOnChannels` allows. Connect one
99
+ * with `POST /v2/agents/{agentId}/channels`.
100
+ */
101
+ channels: Array<AgentChannelsItem>;
102
+ }
103
+
104
+ export interface AgentStats {
105
+ totalInvocations?: number;
106
+ totalTokensUsed?: number;
107
+ /** Total cost in USD. */
108
+ totalCost?: number;
109
+ }
110
+
111
+ export interface AgentChannelsItem {
112
+ address: string;
113
+ type: ChannelType;
114
+ active: boolean;
115
+ }
116
+
117
+ /** Create an agent that exists on its own, disabled and connected to no channel. */
118
+ export interface AgentCreateStandaloneRequest {
119
+ name: string;
120
+ provider: AgentProvider;
121
+ /**
122
+ * Your key for the model provider. Required for `openai`, `anthropic`, `google`
123
+ * and `mistral`, where a request without it is refused with `400`; not used with
124
+ * `zavu`. Stored encrypted and never returned.
125
+ */
126
+ apiKey?: string;
127
+ model: string;
128
+ systemPrompt: string;
129
+ /** @defaultValue `10` */
130
+ contextWindowMessages?: number;
131
+ /** @defaultValue `true` */
132
+ includeContactMetadata?: boolean;
133
+ /** @defaultValue `["*"]` */
134
+ triggerOnChannels?: Array<string>;
135
+ /** @defaultValue `["text"]` */
136
+ triggerOnMessageTypes?: Array<string>;
137
+ maxTokens?: number;
138
+ temperature?: number;
139
+ voice?: AgentVoiceConfig;
140
+ }
141
+
142
+ /** LLM provider for the AI agent. */
143
+ export type AgentProvider = 'openai' | 'anthropic' | 'google' | 'mistral' | 'zavu';
144
+
145
+ /**
146
+ * Run the agent and return what it would say. Nothing is delivered to anyone and
147
+ * no execution is recorded.
148
+ *
149
+ * A run is **billed like a live reply**: on an agent with the `zavu` provider it
150
+ * is charged to your balance at the same price per token, and refused with `402
151
+ * insufficient_balance` at a balance of zero. On a bring-your-own-key agent it
152
+ * costs your provider, not Zavu. `cost` on the response is what this run charged.
153
+ */
154
+ export interface AgentTestRequest {
155
+ /** What to say to the agent. */
156
+ message: string;
157
+ /**
158
+ * Prior turns, oldest first, to exercise multi-turn behaviour without persisting a
159
+ * thread. Trimmed to the agent's context window.
160
+ */
161
+ history?: Array<AgentTestRequestHistoryItem>;
162
+ /**
163
+ * Set false to skip retrieval and isolate prompt behaviour from the knowledge
164
+ * base.
165
+ *
166
+ * @defaultValue `true`
167
+ */
168
+ useKnowledgeBase?: boolean;
169
+ /**
170
+ * Run the tools the agent calls instead of reporting the choice and stopping.
171
+ *
172
+ * Off by default because a tool handler talks to the outside world: a rehearsal
173
+ * that charges a card is not a rehearsal. Turn it on to exercise the loop that
174
+ * actually matters — the model picks a tool, the handler answers, the model
175
+ * replies with the result — without sending a message to anyone. What ran comes
176
+ * back in `executedToolCalls`.
177
+ *
178
+ * @defaultValue `false`
179
+ */
180
+ executeTools?: boolean;
181
+ }
182
+
183
+ export interface AgentTestRequestHistoryItem {
184
+ role: AgentTestRequestHistoryItemRole;
185
+ content: string;
186
+ }
187
+
188
+ export type AgentTestRequestHistoryItemRole = 'user' | 'assistant';
189
+
190
+ export interface AgentTestResponse {
191
+ success: boolean;
192
+ /** What the agent would reply. */
193
+ text: string | null;
194
+ error: string | null;
195
+ inputTokens: number;
196
+ outputTokens: number;
197
+ latencyMs: number;
198
+ /**
199
+ * Knowledge-base chunks retrieved for this message. Zero means the answer was not
200
+ * grounded in your documents.
201
+ */
202
+ knowledgeChunksUsed: number;
203
+ /**
204
+ * Tools that actually ran, in order, when the request set `executeTools`. Empty on
205
+ * a normal dry run, where nothing is executed. An entry with `ok: false` means the
206
+ * agent saw an error and answered around it, which is what a customer would have
207
+ * received.
208
+ */
209
+ executedToolCalls?: Array<AgentTestResponseExecutedToolCallsItem>;
210
+ /**
211
+ * Things that are true of this agent but that a dry run cannot prove. Surfaced so
212
+ * a passing dry run is never mistaken for proof that the agent works live.
213
+ *
214
+ * - The agent being disabled.
215
+ * - Enabled tools that were offered to the model but not run: if the agent picks
216
+ * one, the reply stops at that choice (see `plannedToolCalls`). Pass
217
+ * `executeTools` to run them.
218
+ * - Tools that ran for real, when `executeTools` was set, and any that failed.
219
+ * - An agent whose sender has none of the channels it triggers on, which answers
220
+ * every dry run and no real message.
221
+ * - Contact metadata that exists on a real conversation but not here, and memory
222
+ * tools (`remember`, `recall`, `save_record`), which have no contact to write
223
+ * about on a test run.
224
+ */
225
+ warnings: Array<string>;
226
+ /**
227
+ * USD charged to your balance for this run, rounded to 6 decimals. 0 on a
228
+ * bring-your-own-key agent. When the balance covers only part of a run, that part
229
+ * is charged and the next run is refused with `402`.
230
+ */
231
+ cost: number;
232
+ /**
233
+ * Tools the model chose to call, with the arguments it chose, on a run without
234
+ * `executeTools`. Nothing was executed: the reply stops at the choice. Empty when
235
+ * it answered without reaching for a tool, and on a run that executed tools (see
236
+ * `executedToolCalls`).
237
+ */
238
+ plannedToolCalls: Array<AgentTestResponsePlannedToolCallsItem>;
239
+ }
240
+
241
+ export interface AgentTestResponseExecutedToolCallsItem {
242
+ name: string;
243
+ ok: boolean;
244
+ error?: string | null;
245
+ }
246
+
247
+ export interface AgentTestResponsePlannedToolCallsItem {
248
+ name: string;
249
+ /** The arguments as a JSON string. */
250
+ arguments: string;
251
+ }
252
+
253
+ export interface AgentUpdateRequest {
254
+ name?: string;
255
+ enabled?: boolean;
256
+ provider?: AgentProvider;
257
+ model?: string;
258
+ systemPrompt?: string;
259
+ apiKey?: string;
260
+ contextWindowMessages?: number;
261
+ includeContactMetadata?: boolean;
262
+ maxTokens?: number | null;
263
+ temperature?: number | null;
264
+ triggerOnChannels?: Array<string>;
265
+ triggerOnMessageTypes?: Array<string>;
266
+ /**
267
+ * Voice Agent configuration. Patch this object to enable voice, change the
268
+ * greeting, or adjust call limits. Requires the Voice Agents feature to be enabled
269
+ * for your team.
270
+ */
271
+ voice?: AgentVoiceConfig;
272
+ }
273
+
274
+ /**
275
+ * Voice Agent configuration on an AI agent. Controls how the agent behaves on
276
+ * inbound and outbound phone calls through Zavu's managed voice pipeline (speech
277
+ * recognition, the agent's LLM, and speech synthesis, with real-time interruption
278
+ * handling). Requires the Voice Agents feature to be enabled for your team.
279
+ */
280
+ export interface AgentVoiceConfig {
281
+ /**
282
+ * Whether the agent handles voice calls. When false, the voice agent does not
283
+ * answer calls and outbound calls are rejected.
284
+ */
285
+ enabled: boolean;
286
+ /**
287
+ * Opening line the agent speaks when the call connects. If omitted, the agent
288
+ * waits for the caller to speak first.
289
+ */
290
+ greeting?: string;
291
+ /**
292
+ * Greeting per language, keyed by language code. Used when the caller's language
293
+ * differs from the one `greeting` is written in.
294
+ */
295
+ greetings?: { [key: string]: string };
296
+ /**
297
+ * BCP-47 language code used for both speech recognition and speech synthesis (e.g.
298
+ * `en`, `es`, `pt-BR`). Auto-detected from the recipient when omitted.
299
+ */
300
+ language?: string;
301
+ /**
302
+ * Model that runs the conversation, co-located in the voice network for lowest
303
+ * latency. Independent of the model used for text messaging. Derived from the
304
+ * agent's text model when omitted.
305
+ */
306
+ model?: string;
307
+ /** Speech-recognition provider. Uses the default when omitted. */
308
+ sttProvider?: string;
309
+ /** Speech-recognition model. Uses the default when omitted. */
310
+ sttModel?: string;
311
+ /** Speech-synthesis provider. Uses the default when omitted. */
312
+ ttsProvider?: string;
313
+ /**
314
+ * Identifier of the synthesized voice that speaks. Choose from the voices
315
+ * available in the dashboard. Uses a neutral default when omitted.
316
+ */
317
+ ttsVoiceId?: string;
318
+ /**
319
+ * Speech rate. 1.0 is natural. Only honoured by voices that support rate control;
320
+ * ignored by the others.
321
+ *
322
+ * @defaultValue `1.0`
323
+ */
324
+ voiceSpeed?: number;
325
+ /**
326
+ * Whether the caller can interrupt the agent while it is speaking (barge-in). When
327
+ * true, the agent stops talking as soon as the caller starts.
328
+ *
329
+ * @defaultValue `true`
330
+ */
331
+ interruptible?: boolean;
332
+ /**
333
+ * Hard limit on call length in minutes. The call ends automatically when reached.
334
+ *
335
+ * @defaultValue `15`
336
+ */
337
+ maxCallDurationMinutes?: number;
338
+ /**
339
+ * How long the agent waits during silence before ending the call.
340
+ *
341
+ * @defaultValue `30`
342
+ */
343
+ maxIdleSeconds?: number;
344
+ /** Whether the call audio is recorded. */
345
+ recordCalls?: boolean;
346
+ /**
347
+ * What the agent does when an answering machine or voicemail is detected on an
348
+ * outbound call.
349
+ *
350
+ * @defaultValue `"hangup"`
351
+ */
352
+ voicemailAction?: AgentVoiceConfigVoicemailAction;
353
+ /**
354
+ * Message spoken when `voicemailAction` is `leave_message`. Falls back to
355
+ * `greeting` when omitted.
356
+ */
357
+ voicemailMessage?: string;
358
+ /**
359
+ * E.164 phone number the agent can transfer the call to. When set, the agent is
360
+ * given a transfer tool it can use to hand the call to a human.
361
+ */
362
+ transferPhoneNumber?: string;
363
+ }
364
+
365
+ export type AgentVoiceConfigVoicemailAction = 'hangup' | 'leave_message';
366
+
367
+ export interface AvailablePhoneNumber {
368
+ phoneNumber: string;
369
+ friendlyName?: string;
370
+ locality?: string;
371
+ region?: string;
372
+ capabilities: PhoneNumberCapabilities;
373
+ pricing: PhoneNumberPricing;
374
+ }
375
+
376
+ /**
377
+ * A campaign. The same object as a /v1 broadcast: its `id` works on
378
+ * `/v1/broadcasts/{broadcastId}`.
379
+ */
380
+ export interface Broadcast {
381
+ id: string;
382
+ name: string;
383
+ status: BroadcastStatus;
384
+ channel: BroadcastChannel;
385
+ messageType: BroadcastMessageType;
386
+ text?: string;
387
+ content?: BroadcastContent;
388
+ emailSubject?: string;
389
+ /** Total number of contacts in the broadcast. */
390
+ totalContacts: number;
391
+ pendingCount?: number;
392
+ sendingCount?: number;
393
+ /**
394
+ * Recipients whose message the provider accepted, without a confirmed delivery
395
+ * yet. Channels that never report delivery keep their recipients here.
396
+ */
397
+ sentCount?: number;
398
+ /** Recipients with confirmed delivery to the device. */
399
+ deliveredCount?: number;
400
+ failedCount?: number;
401
+ /** Estimated total cost in USD. */
402
+ estimatedCost?: number | null;
403
+ /** Amount reserved from balance in USD. */
404
+ reservedAmount?: number | null;
405
+ /** Actual cost so far in USD. */
406
+ actualCost?: number | null;
407
+ scheduledAt?: string;
408
+ startedAt?: string;
409
+ completedAt?: string;
410
+ metadata?: { [key: string]: string };
411
+ createdAt: string;
412
+ updatedAt?: string;
413
+ /** AI content review result. */
414
+ reviewResult?: BroadcastReviewResult | null;
415
+ /** Number of review attempts (max 3). */
416
+ reviewAttempts?: number | null;
417
+ /**
418
+ * The address the campaign is sent from, for its `channel` (the sender's first
419
+ * address when `channel` is `smart`).
420
+ */
421
+ from?: string | null;
422
+ }
423
+
424
+ export interface BroadcastReviewResult {
425
+ /** Content safety score from 0.0 to 1.0, where 1.0 is completely safe. */
426
+ score?: number;
427
+ /** Policy categories violated, if any. */
428
+ categories?: Array<string>;
429
+ /** Explanation of the review decision. */
430
+ reasoning?: string;
431
+ reviewedAt?: string;
432
+ /** Problematic text fragments, if any. */
433
+ flaggedContent?: Array<string> | null;
434
+ }
435
+
436
+ /** Broadcast delivery channel. Use 'smart' for per-contact intelligent routing. */
437
+ export type BroadcastChannel = 'smart' | 'sms' | 'sms_oneway' | 'whatsapp' | 'telegram' | 'email';
438
+
439
+ export interface BroadcastContactInput {
440
+ /**
441
+ * Phone number (E.164) or email address. On a `telegram` broadcast, the numeric
442
+ * chat ID of someone who has already written to your bot (`838360395`, or
443
+ * `telegram:838360395`); a phone number is rejected there, because a bot cannot
444
+ * start a chat.
445
+ */
446
+ recipient: string;
447
+ /**
448
+ * Per-contact body variables. Key them to match the template body: by position
449
+ * (`1`, `2`, ...) for positional templates, or by name (e.g. `customer_name`) for
450
+ * named templates. Zavu detects the template's format and sends the correct
451
+ * payload to Meta. Do not mix positional and named keys.
452
+ */
453
+ templateVariables?: { [key: string]: string };
454
+ /**
455
+ * Per-contact button variables for dynamic URL/OTP buttons. Keys are the button
456
+ * index (0, 1, 2).
457
+ */
458
+ templateButtonVariables?: { [key: string]: string };
459
+ /**
460
+ * Per-contact value for a text-header variable, keyed by `1`. If omitted, Zavu
461
+ * resolves the header from `templateVariables` by the header placeholder's name.
462
+ */
463
+ templateHeaderVariables?: { [key: string]: string };
464
+ }
465
+
466
+ export interface BroadcastContactsAddRequest {
467
+ /** List of contacts to add (max 1000 per request). */
468
+ contacts: Array<BroadcastContactInput>;
469
+ }
470
+
471
+ export interface BroadcastContactsAddResponse {
472
+ /** Number of contacts successfully added. */
473
+ added: number;
474
+ /** Number of duplicate contacts skipped. */
475
+ duplicates: number;
476
+ /** Number of invalid contacts rejected. */
477
+ invalid: number;
478
+ /** Details about invalid contacts. */
479
+ errors?: Array<BroadcastContactsAddResponseErrorsItem>;
480
+ }
481
+
482
+ export interface BroadcastContactsAddResponseErrorsItem {
483
+ recipient?: string;
484
+ /**
485
+ * Why it was not added, in words that are true of this recipient and this
486
+ * campaign.
487
+ */
488
+ reason?: string;
489
+ /**
490
+ * Set for `unreachable_by_channel` and `ten_dlc_required`. Absent on other
491
+ * reasons.
492
+ */
493
+ code?: BroadcastContactsAddResponseErrorsItemCode;
494
+ }
495
+
496
+ export type BroadcastContactsAddResponseErrorsItemCode = 'unreachable_by_channel' | 'ten_dlc_required';
497
+
498
+ /** Content for non-text broadcast message types. */
499
+ export interface BroadcastContent {
500
+ /** URL of the media file. */
501
+ mediaUrl?: string;
502
+ /** Media ID if already uploaded. */
503
+ mediaId?: string;
504
+ /** MIME type of the media. */
505
+ mimeType?: string;
506
+ /** Filename for documents. */
507
+ filename?: string;
508
+ /** Template ID for template messages. */
509
+ templateId?: string;
510
+ /**
511
+ * Default body variables (can be overridden per contact). Key them to match the
512
+ * template body: by position (`1`, `2`, ...) for positional templates, or by name
513
+ * (e.g. `customer_name`) for named templates. Zavu detects the template's format
514
+ * and sends the correct payload to Meta. Do not mix positional and named keys.
515
+ */
516
+ templateVariables?: { [key: string]: string };
517
+ /**
518
+ * Default button variables for dynamic URL/OTP buttons. Keys are the button index
519
+ * (0, 1, 2). Per-contact values override these.
520
+ */
521
+ templateButtonVariables?: { [key: string]: string };
522
+ /**
523
+ * Default value for a text-header variable, keyed by `1` (can be overridden per
524
+ * contact). If omitted, Zavu resolves the header from `templateVariables` by the
525
+ * header placeholder's name.
526
+ */
527
+ templateHeaderVariables?: { [key: string]: string };
528
+ }
529
+
530
+ /** A campaign is a /v1 broadcast. Create it, add recipients, then send. */
531
+ export interface BroadcastCreateRequest {
532
+ /** Name of the broadcast campaign. */
533
+ name: string;
534
+ channel: BroadcastChannel;
535
+ /** @defaultValue `"text"` */
536
+ messageType?: BroadcastMessageType;
537
+ /** Text content or caption. Supports template variables: {{name}}, {{1}}, etc. */
538
+ text?: string;
539
+ content?: BroadcastContent;
540
+ /** Email subject line. Required for email broadcasts. */
541
+ emailSubject?: string;
542
+ /** HTML body for email broadcasts. */
543
+ emailHtmlBody?: string;
544
+ /** Schedule the broadcast for future delivery. */
545
+ scheduledAt?: string;
546
+ metadata?: { [key: string]: string };
547
+ /** Idempotency key to prevent duplicate broadcasts. */
548
+ idempotencyKey?: string;
549
+ /**
550
+ * The address that sends the campaign (`GET /v2/channels`). The project default
551
+ * when omitted. Resolved like `from` on `POST /v2/messages`, with the campaign's
552
+ * `channel` as the transport (none for `smart`).
553
+ */
554
+ from?: string;
555
+ }
556
+
557
+ /** Type of message for broadcast. */
558
+ export type BroadcastMessageType = 'text' | 'image' | 'video' | 'audio' | 'document' | 'template';
559
+
560
+ export interface BroadcastProgress {
561
+ status: BroadcastStatus;
562
+ /** Total contacts in broadcast. */
563
+ total: number;
564
+ /** Not yet queued for sending. */
565
+ pending: number;
566
+ /** Currently being sent. */
567
+ sending: number;
568
+ /** Accepted by the provider, delivery not confirmed yet. */
569
+ sent?: number;
570
+ /** Confirmed delivered to the device. */
571
+ delivered: number;
572
+ /** Failed to deliver. */
573
+ failed: number;
574
+ /** Skipped (broadcast cancelled). */
575
+ skipped: number;
576
+ /** Percentage complete (0-100). */
577
+ percentComplete: number;
578
+ /** Estimated total cost in USD. */
579
+ estimatedCost?: number | null;
580
+ /** Amount reserved from balance in USD. */
581
+ reservedAmount?: number | null;
582
+ /** Actual cost so far in USD. */
583
+ actualCost?: number | null;
584
+ startedAt?: string;
585
+ estimatedCompletionAt?: string;
586
+ campaignId: string;
587
+ }
588
+
589
+ export interface BroadcastSendRequest {
590
+ /** Schedule for future delivery. Omit to send immediately. */
591
+ scheduledAt?: string;
592
+ }
593
+
594
+ /**
595
+ * Current status of the campaign. `pending_review`: in the automated content
596
+ * review. `pending_admin_review`: the automated review passed and it waits for a
597
+ * person, which is where every channel but a WhatsApp campaign on an approved
598
+ * template goes. `rejected`: the review refused it; edit it and send again, or
599
+ * escalate. `scheduled` and `sending` follow approval.
600
+ */
601
+ export type BroadcastStatus =
602
+ | 'draft'
603
+ | 'pending_review'
604
+ | 'pending_admin_review'
605
+ | 'approved'
606
+ | 'rejected'
607
+ | 'escalated'
608
+ | 'rejected_final'
609
+ | 'scheduled'
610
+ | 'sending'
611
+ | 'paused'
612
+ | 'completed'
613
+ | 'cancelled'
614
+ | 'failed';
615
+
616
+ /** A transport a message travels on. */
617
+ export type ChannelType =
618
+ | 'sms'
619
+ | 'sms_oneway'
620
+ | 'whatsapp'
621
+ | 'telegram'
622
+ | 'email'
623
+ | 'instagram'
624
+ | 'messenger'
625
+ | 'voice';
626
+
627
+ /**
628
+ * `auto`, or a transport. `auto` applies the fixed order described on `POST
629
+ * /v2/messages`.
630
+ */
631
+ export type ChannelTypeRequest =
632
+ | 'auto'
633
+ | 'sms'
634
+ | 'sms_oneway'
635
+ | 'whatsapp'
636
+ | 'telegram'
637
+ | 'email'
638
+ | 'instagram'
639
+ | 'messenger'
640
+ | 'voice';
641
+
642
+ export interface Contact {
643
+ id: string;
644
+ /** Display name for the contact. */
645
+ displayName?: string;
646
+ /** DEPRECATED: Use primaryPhone instead. Primary phone number in E.164 format. */
647
+ phoneNumber?: string;
648
+ /** Primary phone number in E.164 format. */
649
+ primaryPhone?: string;
650
+ /** Primary email address. */
651
+ primaryEmail?: string;
652
+ countryCode?: string;
653
+ /** Contact's WhatsApp profile name. Only available for WhatsApp contacts. */
654
+ profileName?: string | null;
655
+ /** List of available messaging channels for this contact. */
656
+ availableChannels: Array<string>;
657
+ /** Preferred channel for this contact. */
658
+ defaultChannel?: ContactDefaultChannel;
659
+ /** Whether this contact has been verified. */
660
+ verified: boolean;
661
+ /** All communication channels for this contact. */
662
+ channels?: Array<ContactChannel>;
663
+ metadata: { [key: string]: string };
664
+ createdAt: string;
665
+ updatedAt?: string;
666
+ }
667
+
668
+ export type ContactDefaultChannel =
669
+ | 'sms'
670
+ | 'whatsapp'
671
+ | 'telegram'
672
+ | 'email'
673
+ | 'instagram'
674
+ | 'messenger'
675
+ | 'voice';
676
+
677
+ /** A communication channel for a contact. */
678
+ export interface ContactChannel {
679
+ id: string;
680
+ /** Channel type. */
681
+ channel: ContactChannelChannel;
682
+ /** Channel identifier (phone number or email address). */
683
+ identifier: string;
684
+ /** ISO country code for phone numbers. */
685
+ countryCode?: string;
686
+ /** Optional label for the channel. */
687
+ label?: string;
688
+ /** Whether this is the primary channel for its type. */
689
+ isPrimary: boolean;
690
+ /** Whether this channel has been verified. */
691
+ verified: boolean;
692
+ /** Delivery metrics for this channel. */
693
+ metrics?: ContactChannelMetrics;
694
+ /** Last time a message was received on this channel. */
695
+ lastInboundAt?: string;
696
+ metadata?: { [key: string]: string };
697
+ createdAt: string;
698
+ updatedAt?: string;
699
+ }
700
+
701
+ export type ContactChannelChannel =
702
+ | 'sms'
703
+ | 'whatsapp'
704
+ | 'email'
705
+ | 'telegram'
706
+ | 'instagram'
707
+ | 'messenger'
708
+ | 'voice';
709
+
710
+ export interface ContactChannelMetrics {
711
+ successCount?: number;
712
+ failureCount?: number;
713
+ totalAttempts?: number;
714
+ avgDeliveryTimeMs?: number;
715
+ lastSuccessAt?: string;
716
+ }
717
+
718
+ /** Input for creating a contact channel. */
719
+ export interface ContactChannelInput {
720
+ /** Channel type. */
721
+ channel: ContactChannelInputChannel;
722
+ /** Channel identifier (phone number in E.164 format or email address). */
723
+ identifier: string;
724
+ /** ISO country code for phone numbers. */
725
+ countryCode?: string;
726
+ /** Optional label for the channel. */
727
+ label?: string;
728
+ /**
729
+ * Whether this should be the primary channel for its type.
730
+ *
731
+ * @defaultValue `false`
732
+ */
733
+ isPrimary?: boolean;
734
+ }
735
+
736
+ export type ContactChannelInputChannel =
737
+ | 'sms'
738
+ | 'whatsapp'
739
+ | 'email'
740
+ | 'telegram'
741
+ | 'instagram'
742
+ | 'messenger'
743
+ | 'voice';
744
+
745
+ /** Request body to create a contact with channels. */
746
+ export interface ContactCreateRequest {
747
+ /** Display name for the contact. */
748
+ displayName?: string;
749
+ /** Communication channels for the contact. */
750
+ channels: Array<ContactChannelInput>;
751
+ /** Arbitrary metadata to associate with the contact. */
752
+ metadata?: { [key: string]: string };
753
+ }
754
+
755
+ export interface ContactUpdateRequest {
756
+ /**
757
+ * Human-readable name for this contact. Set to null to clear it and fall back to
758
+ * the contact's identifier. Contacts created automatically from an inbound message
759
+ * have no display name until you set one.
760
+ */
761
+ displayName?: string | null;
762
+ /** Preferred channel for this contact. Set to null to clear. */
763
+ defaultChannel?: ContactUpdateRequestDefaultChannel | null;
764
+ metadata?: { [key: string]: string };
765
+ }
766
+
767
+ export type ContactUpdateRequestDefaultChannel =
768
+ | 'sms'
769
+ | 'whatsapp'
770
+ | 'telegram'
771
+ | 'email'
772
+ | 'instagram'
773
+ | 'messenger'
774
+ | 'voice';
775
+
776
+ /**
777
+ * An inbox thread with one contact. A conversation groups every message exchanged
778
+ * with that contact across channels, so a contact who writes on WhatsApp and later
779
+ * by email stays in one thread.
780
+ */
781
+ export interface Conversation {
782
+ id: string;
783
+ /**
784
+ * ID of the contact this thread belongs to. Absent on group threads and on threads
785
+ * whose contact has not been resolved yet.
786
+ */
787
+ contactId?: string;
788
+ /**
789
+ * The key this thread is filed under: a phone number in E.164, a WhatsApp
790
+ * business-scoped user ID (BSUID), or a numeric chat ID
791
+ * (Telegram/Instagram/Messenger). It is not always a phone number, so do not parse
792
+ * it as one.
793
+ */
794
+ contactIdentifier: string;
795
+ /** Email address of the thread, when the contact was reached by email. */
796
+ email?: string;
797
+ /** Every channel this thread has carried messages on. */
798
+ channels: Array<string>;
799
+ /**
800
+ * Denormalized preview of the most recent message, so a thread list needs no extra
801
+ * fetch.
802
+ */
803
+ lastMessage: ConversationLastMessage;
804
+ messageCount: number;
805
+ /**
806
+ * Inbound messages not yet marked read. Reset with POST
807
+ * /v2/conversations/{conversationId}/read.
808
+ */
809
+ unreadCount: number;
810
+ /** WhatsApp identity, present when the contact adopted a username. */
811
+ whatsapp?: ConversationWhatsapp;
812
+ createdAt: string;
813
+ updatedAt: string;
814
+ /**
815
+ * The address this thread was last handled from, on the transport of its last
816
+ * message. Pass the conversation's `id` as `conversationId` on `POST /v2/messages`
817
+ * to reply from it. A thread opened by an API send before /v2 recorded no sender;
818
+ * its `from` is read off its messages. `?from=` on the list matches threads by the
819
+ * recorded sender only, which messages sent through /v2 record.
820
+ */
821
+ from?: string | null;
822
+ }
823
+
824
+ export interface ConversationLastMessage {
825
+ id: string;
826
+ /** Text or caption. Empty when the last message carried no text (e.g. media). */
827
+ text: string;
828
+ channel: ChannelType;
829
+ direction: ConversationLastMessageDirection;
830
+ at: string;
831
+ }
832
+
833
+ export type ConversationLastMessageDirection = 'inbound' | 'outbound';
834
+
835
+ export interface ConversationWhatsapp {
836
+ /** Business-scoped user ID. Can be used as `to` when sending. */
837
+ bsuid?: string;
838
+ username?: string;
839
+ }
840
+
841
+ /** Email attachment. Provide either `content` (base64) or `path` (URL), not both. */
842
+ export interface EmailAttachmentInput {
843
+ /** Name of the attached file. */
844
+ filename: string;
845
+ /** Content of the attached file as a Base64-encoded string. */
846
+ content?: string;
847
+ /** URL where the attachment file is hosted. The server will fetch the file. */
848
+ path?: string;
849
+ /** MIME type of the attachment. If not set, will be derived from the filename. */
850
+ content_type?: string;
851
+ /**
852
+ * Content ID for inline images. Reference in HTML as `<img
853
+ * src="cid:your_content_id">`.
854
+ */
855
+ content_id?: string;
856
+ }
857
+
858
+ /**
859
+ * Every v2 error has this shape at the root. There is no `{error: {...}}` wrapper
860
+ * anywhere in v2. A missing resource is `404 not_found` with the message
861
+ * `<Resource> not found`; an address the project does not have is `404 not_found`,
862
+ * `None of this project's channels has the address <address>.`, with the address
863
+ * in `details` under the name the request used (`from` or `address`). A `message`
864
+ * is written for the caller and never carries a stack trace or a source path. A
865
+ * body that is not JSON is `400 invalid_json`, with `details.errors` (`path: []`,
866
+ * the parse error in `message`).
867
+ */
868
+ export interface ErrorBody {
869
+ code: string;
870
+ message: string;
871
+ /**
872
+ * Machine-readable context. Keys that mean the same thing everywhere:
873
+ * - `errors`: present on every `400 invalid_request` for an invalid body or query
874
+ * parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`,
875
+ * `["contacts", 2, "recipient"]`, `["limit"]`).
876
+ * - `recommendation`: one sentence saying what to do next.
877
+ * - `dashboardUrl`: the dashboard screen that fixes it.
878
+ * - `connect`: on `409 no_channel`, the channels this project can connect to reach
879
+ * the recipient, each `{type, request, note, available, dashboardUrl?}`;
880
+ * `request` is the exact call (`POST /v2/channels` with its body, or `POST
881
+ * /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp,
882
+ * Instagram, Messenger). `available: false` says why in `note` (one-way SMS
883
+ * closed until business verification is approved).
884
+ * - `address` / `from`: the address the error is about, under the name the request
885
+ * used for it.
886
+ */
887
+ details?: ErrorDetails;
888
+ }
889
+
890
+ export interface ErrorDetails {
891
+ errors?: Array<ErrorDetailsErrorsItem>;
892
+ [key: string]: unknown;
893
+ }
894
+
895
+ export interface ErrorDetailsErrorsItem {
896
+ path: Array<ErrorDetailsErrorsItemPathItem>;
897
+ message: string;
898
+ code?: string;
899
+ [key: string]: unknown;
900
+ }
901
+
902
+ export type ErrorDetailsErrorsItemPathItem = string | number;
903
+
904
+ export interface Message {
905
+ id: string;
906
+ to: string;
907
+ /**
908
+ * Where the message came from: for an outbound message, your address (as `GET
909
+ * /v2/channels` lists it for `channel`, except WhatsApp, where it is the sender's
910
+ * own number field); for an inbound one, the contact.
911
+ */
912
+ from?: string;
913
+ channel: ChannelType;
914
+ /**
915
+ * Who sent the message. Needed to render a thread: `status` cannot tell the two
916
+ * apart, because an inbound message is also stored as `delivered`.
917
+ */
918
+ direction: MessageDirection;
919
+ messageType: MessageType;
920
+ status: MessageStatus;
921
+ /** Text content or caption. */
922
+ text?: string;
923
+ content?: MessageContent;
924
+ /**
925
+ * ID of the conversation (inbox thread) this message belongs to. Use it to build a
926
+ * direct dashboard link:
927
+ * `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`. Omitted only
928
+ * on legacy messages created before conversation threading.
929
+ */
930
+ conversationId?: string;
931
+ /** Message ID from the delivery provider. */
932
+ providerMessageId?: string;
933
+ /**
934
+ * Why a failed message failed, as a Zavu code: `RISK_REJECTED`,
935
+ * `DAILY_LIMIT_EXCEEDED`, `TEN_DLC_REQUIRED`, `SMS_NOT_DELIVERED`,
936
+ * `WHATSAPP_WINDOW_CLOSED`... A delivery provider's own code is translated where
937
+ * its meaning is known and kept in `errorDetails.providerCode`; one with no known
938
+ * meaning is `SMS_DELIVERY_FAILED`, `WHATSAPP_REJECTED`, `VOICE_DELIVERY_FAILED`,
939
+ * `EMAIL_DELIVERY_FAILED` or `DELIVERY_FAILED`. (/v1 returns the provider's raw
940
+ * code here.)
941
+ */
942
+ errorCode?: string | null;
943
+ errorMessage?: string | null;
944
+ /**
945
+ * Zavu's own per-message platform charge, in USD (6 decimals). Messaging is billed
946
+ * against the plan's allowance, so it is often 0.
947
+ */
948
+ cost?: number | null;
949
+ /**
950
+ * What delivering the message cost Zavu (carrier cost), in USD. Filled in when the
951
+ * delivery report brings it, so null until then.
952
+ */
953
+ costProvider?: number | null;
954
+ /**
955
+ * `cost` + `costProvider`. For SMS this is BEFORE Zavu's markup, so it is not what
956
+ * your balance was charged: read `charged` for that. For voice it includes the
957
+ * markup.
958
+ */
959
+ costTotal?: number | null;
960
+ metadata?: { [key: string]: string };
961
+ createdAt: string;
962
+ updatedAt?: string;
963
+ /**
964
+ * Transports tried before the current `channel`, oldest first, when an automatic
965
+ * fallback moved the message (WhatsApp to SMS). Empty when no fallback happened.
966
+ * Fallback runs after the send returned, so this can change after `POST
967
+ * /v2/messages` answered.
968
+ */
969
+ attemptedChannels?: Array<ChannelType>;
970
+ /**
971
+ * `held` while the message waits for risk review (it stays `queued`), `released`
972
+ * once a reviewer let it go, null when it was never held.
973
+ */
974
+ riskHold?: MessageRiskHold | null;
975
+ /** Present when `errorCode` translates a delivery provider's code. */
976
+ errorDetails?: MessageErrorDetails | null;
977
+ /**
978
+ * What this message has taken from your prepaid balance, in USD. SMS and one-way
979
+ * SMS only: charged per segment when the message is accepted
980
+ * (`routing.estimatedCost`), raised if the delivery report brings a higher real
981
+ * cost, and refunded (0) when the message fails AND the carrier charged nothing. A
982
+ * failed SMS the carrier did charge for (it attempted delivery) stays charged.
983
+ * Null on other channels, which are not billed per message here.
984
+ */
985
+ charged?: number | null;
986
+ }
987
+
988
+ export type MessageDirection = 'inbound' | 'outbound';
989
+
990
+ export type MessageRiskHold = 'held' | 'released';
991
+
992
+ export interface MessageErrorDetails {
993
+ /** The provider's own code, unchanged. */
994
+ providerCode?: string;
995
+ }
996
+
997
+ /** Content for non-text message types (WhatsApp and Telegram). */
998
+ export interface MessageContent {
999
+ /** URL of the media file (for image, video, audio, document, sticker). */
1000
+ mediaUrl?: string;
1001
+ /** WhatsApp media ID if already uploaded. */
1002
+ mediaId?: string;
1003
+ /** MIME type of the media. */
1004
+ mimeType?: string;
1005
+ /** Filename for documents. */
1006
+ filename?: string;
1007
+ /** Latitude for location messages. */
1008
+ latitude?: number;
1009
+ /** Longitude for location messages. */
1010
+ longitude?: number;
1011
+ /** Name of the location. */
1012
+ locationName?: string;
1013
+ /** Address of the location. */
1014
+ locationAddress?: string;
1015
+ /** Contact cards for contact messages. */
1016
+ contacts?: Array<MessageContentContactsItem>;
1017
+ /** Interactive buttons (max 3). */
1018
+ buttons?: Array<MessageContentButtonsItem>;
1019
+ /** Button text for list messages. */
1020
+ listButton?: string;
1021
+ /** Sections for list messages. */
1022
+ sections?: Array<MessageContentSectionsItem>;
1023
+ /** Button label for cta_url messages. */
1024
+ ctaDisplayText?: string;
1025
+ /**
1026
+ * Destination URL opened in the device's default browser when the button is
1027
+ * tapped. Used with messageType=cta_url. WhatsApp requires HTTPS in production.
1028
+ */
1029
+ ctaUrl?: string;
1030
+ /** Optional header type for cta_url messages. */
1031
+ ctaHeaderType?: MessageContentCtaHeaderType;
1032
+ /** Header text when ctaHeaderType is 'text'. */
1033
+ ctaHeaderText?: string;
1034
+ /**
1035
+ * Public HTTPS URL of the header media when ctaHeaderType is 'image', 'video', or
1036
+ * 'document'. WhatsApp fetches this URL — it must be publicly reachable and
1037
+ * return the declared content type.
1038
+ */
1039
+ ctaHeaderMediaUrl?: string;
1040
+ /** Optional footer text for cta_url messages. */
1041
+ footerText?: string;
1042
+ /** Emoji for reaction messages. */
1043
+ emoji?: string;
1044
+ /** Message ID to react to. */
1045
+ reactToMessageId?: string;
1046
+ /**
1047
+ * Zavu message ID of the quoted message this message replies to. Present on
1048
+ * inbound messages that quote an earlier message. Omitted when the quoted message
1049
+ * is not found in Zavu (e.g. an old or unknown message) — use
1050
+ * replyToProviderMessageId in that case.
1051
+ */
1052
+ replyToMessageId?: string;
1053
+ /**
1054
+ * Provider message ID (WhatsApp WAMID) of the quoted message. Present whenever an
1055
+ * inbound message is a reply, even if the quoted message is not stored in Zavu.
1056
+ */
1057
+ replyToProviderMessageId?: string;
1058
+ /** Sender of the quoted message (phone number in E.164 format). */
1059
+ replyToFrom?: string;
1060
+ /**
1061
+ * Truncated snippet of the quoted message's text, for display. Empty when the
1062
+ * quoted message has no text (e.g. media).
1063
+ */
1064
+ replyToText?: string;
1065
+ /** Type of the quoted message (text, image, video, etc.). */
1066
+ replyToMessageType?: string;
1067
+ /** Template ID for template messages. */
1068
+ templateId?: string;
1069
+ /**
1070
+ * Variables for body placeholders. Key them to match the template body: by
1071
+ * position (`1`, `2`, ...) for positional templates, or by name (e.g.
1072
+ * `customer_name`) for named templates. Zavu detects the template's format and
1073
+ * sends the correct payload to Meta. Named keys also resolve a named text-header
1074
+ * variable. Do not mix positional and named keys in the same request.
1075
+ */
1076
+ templateVariables?: { [key: string]: string };
1077
+ /**
1078
+ * Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys
1079
+ * are the button index (0, 1, 2) in the template's `buttons` array — not the
1080
+ * placeholder name. Values substitute the `{{1}}` placeholder inside that button's
1081
+ * URL.
1082
+ *
1083
+ * **WhatsApp constraints:**
1084
+ * - URL buttons only accept `{{1}}` — positional, numeric, no whitespace, no
1085
+ * name. Named placeholders like `{{token}}` are stored as literal URL text by
1086
+ * Meta and cannot be substituted.
1087
+ * - At most one placeholder per URL button.
1088
+ * - A template may have at most three buttons.
1089
+ * - Static URL buttons (no placeholder) and `quick_reply` buttons are not included
1090
+ * here.
1091
+ */
1092
+ templateButtonVariables?: { [key: string]: string };
1093
+ /**
1094
+ * Value for a text-header variable, keyed by `1` (WhatsApp text headers allow at
1095
+ * most one variable). Optional override. If omitted, Zavu resolves the header from
1096
+ * `templateVariables` using the header placeholder's name (e.g. `novios`). Static
1097
+ * text headers need no value.
1098
+ */
1099
+ templateHeaderVariables?: { [key: string]: string };
1100
+ referral?: AdReferral;
1101
+ }
1102
+
1103
+ export interface MessageContentContactsItem {
1104
+ name?: string;
1105
+ phones?: Array<string>;
1106
+ }
1107
+
1108
+ export interface MessageContentButtonsItem {
1109
+ id: string;
1110
+ title: string;
1111
+ }
1112
+
1113
+ export interface MessageContentSectionsItem {
1114
+ title: string;
1115
+ rows: Array<MessageContentSectionsItemRowsItem>;
1116
+ }
1117
+
1118
+ export interface MessageContentSectionsItemRowsItem {
1119
+ id: string;
1120
+ title: string;
1121
+ description?: string;
1122
+ }
1123
+
1124
+ export type MessageContentCtaHeaderType = 'text' | 'image' | 'video' | 'document';
1125
+
1126
+ export type MessageStatus =
1127
+ | 'queued'
1128
+ | 'sending'
1129
+ | 'sent'
1130
+ | 'delivered'
1131
+ | 'read'
1132
+ | 'failed'
1133
+ | 'received'
1134
+ | 'pending_url_verification';
1135
+
1136
+ /**
1137
+ * Type of message. Every type except `text` and `template` is sent on WhatsApp
1138
+ * only: the send routes them there whatever `channel` says, and in v2 an explicit
1139
+ * `channel` other than `whatsapp` is refused with `400` naming `messageType` and
1140
+ * `channel`.
1141
+ *
1142
+ * `location_request` asks the recipient to share their location and is
1143
+ * WhatsApp-only. It takes no `content` object — the prompt goes in `text` (max
1144
+ * 1024 characters) and the button label is fixed by WhatsApp. The recipient's
1145
+ * answer arrives as an inbound `location` message whose `content.replyToMessageId`
1146
+ * is the ID of the request.
1147
+ *
1148
+ * `request_contact_info` asks the recipient to share their phone number and is
1149
+ * WhatsApp-only. Like `location_request` it takes no `content` object — the
1150
+ * prompt goes in `text` (max 1024 characters) and WhatsApp renders a fixed **Share
1151
+ * Contact Info** button. The answer arrives as an inbound `contact` message. Use
1152
+ * it to recover the phone number of a contact who adopted a WhatsApp username and
1153
+ * is only known by their business-scoped user ID (BSUID); when they share it, Zavu
1154
+ * automatically links the phone number to that contact.
1155
+ */
1156
+ export type MessageType =
1157
+ | 'text'
1158
+ | 'image'
1159
+ | 'video'
1160
+ | 'audio'
1161
+ | 'document'
1162
+ | 'sticker'
1163
+ | 'location'
1164
+ | 'contact'
1165
+ | 'buttons'
1166
+ | 'list'
1167
+ | 'cta_url'
1168
+ | 'request_contact_info'
1169
+ | 'location_request'
1170
+ | 'reaction'
1171
+ | 'template';
1172
+
1173
+ export interface OwnedPhoneNumber {
1174
+ id: string;
1175
+ phoneNumber: string;
1176
+ /** Optional custom name for the phone number. */
1177
+ name?: string;
1178
+ capabilities: Array<string>;
1179
+ status: PhoneNumberStatus;
1180
+ pricing: OwnedPhoneNumberPricing;
1181
+ /**
1182
+ * Regulatory review state. Numbers that need no review are `approved` immediately.
1183
+ * A number bought with regulatory information is owned and billed from purchase
1184
+ * and starts `pending_review`; it cannot send messages or place calls until this
1185
+ * is `approved`, and its `channels` entry can be absent until then. The state is
1186
+ * re-checked every 6 hours.
1187
+ */
1188
+ regulatoryStatus: OwnedPhoneNumberRegulatoryStatus;
1189
+ nextRenewalDate?: string;
1190
+ createdAt: string;
1191
+ updatedAt?: string;
1192
+ }
1193
+
1194
+ export type OwnedPhoneNumberRegulatoryStatus = 'approved' | 'pending_review' | 'rejected';
1195
+
1196
+ export interface OwnedPhoneNumberPricing {
1197
+ /** Monthly price in USD. */
1198
+ monthlyPrice?: number;
1199
+ /** One-time purchase cost in cents. */
1200
+ upfrontCost?: number;
1201
+ /** Monthly cost in cents. */
1202
+ monthlyCost?: number;
1203
+ /** Whether this is a free number. */
1204
+ isFreeNumber?: boolean;
1205
+ }
1206
+
1207
+ export interface PhoneNumberCapabilities {
1208
+ sms?: boolean;
1209
+ voice?: boolean;
1210
+ mms?: boolean;
1211
+ }
1212
+
1213
+ export interface PhoneNumberPricing {
1214
+ /** Monthly price in USD. */
1215
+ monthlyPrice?: number;
1216
+ /** One-time purchase price in USD. */
1217
+ upfrontPrice?: number;
1218
+ /**
1219
+ * Whether this number qualifies as the plan-included number: a US or Canadian
1220
+ * number (a +1 number) costing $20 a month or less. The benefit is one per
1221
+ * account: it is never offered again once claimed, not even after the number is
1222
+ * released.
1223
+ */
1224
+ isFreeEligible?: boolean;
1225
+ }
1226
+
1227
+ export interface PhoneNumberPurchaseRequest {
1228
+ /** Phone number in E.164 format. */
1229
+ phoneNumber: string;
1230
+ /** Optional custom name for the phone number. */
1231
+ name?: string;
1232
+ /**
1233
+ * Type the number was found under in `GET /v2/numbers/available`. Used to look the
1234
+ * number up and to keep regulatory information for reuse. Required when
1235
+ * `regulatoryRequirements` is sent. `toll_free` is accepted as an alias of
1236
+ * `tollFree`. A number not listed under this type is refused with `400
1237
+ * number_unavailable` saying so.
1238
+ */
1239
+ type?: PhoneNumberType;
1240
+ /**
1241
+ * Regulatory information, for numbers whose requirements list is not empty. Get
1242
+ * the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and send one
1243
+ * entry per requirement id, except `action` requirements, which take no value.
1244
+ * Every required id must be present, once, and no unknown id may be sent;
1245
+ * otherwise the purchase is refused with `400 invalid_request` before anything is
1246
+ * charged.
1247
+ *
1248
+ * The information is kept for your project under the number's country and `type`.
1249
+ * A later purchase there may omit this field if what is kept still covers that
1250
+ * number's requirements. Omit it for numbers without requirements.
1251
+ */
1252
+ regulatoryRequirements?: Array<PhoneNumberPurchaseRequestRegulatoryRequirementsItem>;
1253
+ }
1254
+
1255
+ export interface PhoneNumberPurchaseRequestRegulatoryRequirementsItem {
1256
+ /**
1257
+ * A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each id may
1258
+ * appear only once.
1259
+ */
1260
+ requirementType: string;
1261
+ /**
1262
+ * Depends on the requirement's `type`: the text itself for `textual`; for
1263
+ * `address`, the `id` of an address created in this project with `POST
1264
+ * /v1/addresses`; for `document`, the `id` of a document created with `POST
1265
+ * /v1/documents`. An address or document from another project, or one rejected in
1266
+ * review, is refused.
1267
+ */
1268
+ fieldValue: string;
1269
+ }
1270
+
1271
+ /**
1272
+ * Billing state of an owned number, separate from `regulatoryStatus`. `pending` is
1273
+ * legacy and is not written to numbers today.
1274
+ */
1275
+ export type PhoneNumberStatus = 'active' | 'suspended' | 'pending' | 'releasing' | 'released';
1276
+
1277
+ /**
1278
+ * Type of phone number. `mobile` is stocked in countries where no geographic
1279
+ * (`local`) or non-geographic (`national`) inventory exists, and in several
1280
+ * markets it is the only type that can receive SMS.
1281
+ */
1282
+ export type PhoneNumberType = 'local' | 'national' | 'tollFree' | 'mobile';
1283
+
1284
+ /**
1285
+ * The one webhook for everything in the project. Stored apart from the /v1 project
1286
+ * webhook (`/v1/invitations/webhook`); neither API reads or writes the other's.
1287
+ */
1288
+ export interface ProjectWebhookV2 {
1289
+ url: string;
1290
+ /**
1291
+ * Any event in `WebhookEvent`, including `message.*`, `conversation.new` and
1292
+ * `call.*`.
1293
+ */
1294
+ events: Array<WebhookEvent>;
1295
+ active: boolean;
1296
+ signatureVersion: WebhookSignatureVersion;
1297
+ /**
1298
+ * Returned only by `PUT /v2/webhook` when it creates the webhook, and by `POST
1299
+ * /v2/webhook/secret`.
1300
+ */
1301
+ secret?: string;
1302
+ }
1303
+
1304
+ export interface ProjectWebhookV2Put {
1305
+ /** HTTPS only. */
1306
+ url: string;
1307
+ /**
1308
+ * Any event in `WebhookEvent`. An unknown value is refused with `400` and
1309
+ * `details.allowed`.
1310
+ */
1311
+ events: Array<WebhookEvent>;
1312
+ /** @defaultValue `true` */
1313
+ active?: boolean;
1314
+ signatureVersion?: WebhookSignatureVersion;
1315
+ /**
1316
+ * Switch off the /v1 sender webhooks of this project in the same call, so each
1317
+ * event arrives once. They are only switched off (`webhookActive: false` on /v1);
1318
+ * URL, events and secret stay, so /v1 can turn one back on. Off by default: the
1319
+ * /v2 webhook is added next to them and both receive every event until you decide.
1320
+ * `senderWebhooks` on the response lists what is still on.
1321
+ *
1322
+ * @defaultValue `false`
1323
+ */
1324
+ disableSenderWebhooks?: boolean;
1325
+ }
1326
+
1327
+ /**
1328
+ * What the engine decided at send time. Reports only decisions the engine makes;
1329
+ * it does not rank channels by price or by WhatsApp window state.
1330
+ */
1331
+ export interface Routing {
1332
+ channel: ChannelType;
1333
+ /**
1334
+ * The address the message leaves from, as `GET /v2/channels` lists it for
1335
+ * `channel`. With a test-mode key, the WhatsApp sandbox number.
1336
+ */
1337
+ from: string;
1338
+ /**
1339
+ * Why this transport was chosen. Closed list:
1340
+ * - `explicit`: `channel` was given.
1341
+ * - `auto_order`: `channel` omitted or `auto`; the first ACTIVE transport of the
1342
+ * sender in a fixed order (for a phone number: WhatsApp, then SMS, then one-way
1343
+ * SMS). A connected transport that is switched off is skipped, not chosen and
1344
+ * then refused. It does not compare price and does not check the WhatsApp
1345
+ * 24-hour window; a closed window fails later as `message.failed`.
1346
+ * - `conversation`: the sender came from `conversationId` (the sender, not the
1347
+ * transport; the transport is then chosen as for `auto_order` unless `channel`
1348
+ * is given).
1349
+ * - `recipient_type`: `to` is an email address, so email.
1350
+ * - `message_type`: `messageType` is WhatsApp-only (media, interactive,
1351
+ * template...), so WhatsApp.
1352
+ * - `sandbox`: test-mode key, which always uses the WhatsApp sandbox.
1353
+ * An automatic fallback is never a send-time reason: it happens later, see
1354
+ * `Message.attemptedChannels`.
1355
+ */
1356
+ reason: RoutingReason;
1357
+ /**
1358
+ * USD this call took from the balance when it was accepted (rounded to 6
1359
+ * decimals). An estimate: for SMS the delivery report can raise it to the real
1360
+ * cost, and `message.charged` shows where it ended up. Present for `sms` and
1361
+ * `sms_oneway`. Null otherwise — WhatsApp is billed by Meta, email in
1362
+ * 1,000-message blocks, voice per minute — and also null while a message waits
1363
+ * for URL verification, which is charged when it is released. Null means "not
1364
+ * known at send time", not "free".
1365
+ */
1366
+ estimatedCost: number | null;
1367
+ }
1368
+
1369
+ export type RoutingReason =
1370
+ | 'explicit'
1371
+ | 'auto_order'
1372
+ | 'conversation'
1373
+ | 'recipient_type'
1374
+ | 'message_type'
1375
+ | 'sandbox';
1376
+
1377
+ /**
1378
+ * Request body to send a message. No `Zavu-Sender` header: where it leaves from
1379
+ * comes from `conversationId`, then `from`, then the project default. `to` is
1380
+ * required, except with `conversationId`, where it defaults to whoever the
1381
+ * conversation is with.
1382
+ */
1383
+ export interface SendMessageRequest {
1384
+ /**
1385
+ * Recipient phone number in E.164 format, email address, WhatsApp business-scoped
1386
+ * user ID (BSUID, e.g. `US.13491208655302741918`), or numeric chat ID (for
1387
+ * Telegram/Instagram/Messenger). A phone number written with spaces, dashes, dots
1388
+ * or parentheses after its `+` (`+56 9 1234 5678`) is read as the E.164 number;
1389
+ * bare digits are not (see `400` on this operation). A BSUID is routed to WhatsApp
1390
+ * and sent via the `recipient` field; use it to message a contact who adopted a
1391
+ * username and whose phone number is hidden.
1392
+ *
1393
+ * Required unless `conversationId` is given. With `conversationId` it may be
1394
+ * omitted (it is the conversation's contact: its email for `channel: "email"`,
1395
+ * else the identifier it is filed under); if given it must be one of that
1396
+ * conversation's identifiers (its key, email or BSUID, in any formatting), else
1397
+ * `400` naming `to` and `conversationId`.
1398
+ */
1399
+ to?: string;
1400
+ channel?: ChannelTypeRequest;
1401
+ /**
1402
+ * Type of message. Defaults to 'text'.
1403
+ *
1404
+ * @defaultValue `"text"`
1405
+ */
1406
+ messageType?: MessageType;
1407
+ /** Text body for text messages or caption for media messages. */
1408
+ text?: string;
1409
+ /** Additional content for non-text message types. */
1410
+ content?: MessageContent;
1411
+ /**
1412
+ * Email subject line. Required when channel is 'email' or recipient is an email
1413
+ * address.
1414
+ */
1415
+ subject?: string;
1416
+ /**
1417
+ * HTML body for email messages. If provided, email will be sent as multipart with
1418
+ * both text and HTML.
1419
+ */
1420
+ htmlBody?: string;
1421
+ /** Reply-To email address for email messages. */
1422
+ replyTo?: string;
1423
+ /** Optional idempotency key to avoid duplicate sends. */
1424
+ idempotencyKey?: string;
1425
+ /** Arbitrary metadata to associate with the message. */
1426
+ metadata?: { [key: string]: string };
1427
+ /**
1428
+ * Whether to enable automatic fallback to SMS if WhatsApp fails. Defaults to true.
1429
+ *
1430
+ * @defaultValue `true`
1431
+ */
1432
+ fallbackEnabled?: boolean;
1433
+ /**
1434
+ * Language code for voice text-to-speech (e.g., 'en-US', 'es-ES', 'pt-BR'). If
1435
+ * omitted, language is auto-detected from recipient's country code.
1436
+ */
1437
+ voiceLanguage?: string;
1438
+ /**
1439
+ * Email attachments. Only supported when channel is 'email'. Maximum 40MB total
1440
+ * size.
1441
+ */
1442
+ attachments?: Array<EmailAttachmentInput>;
1443
+ /**
1444
+ * Which of your addresses sends (see `GET /v2/channels`): an E.164 number in any
1445
+ * common formatting, an email address, `@botname`, `@username`, a Page id.
1446
+ * Resolved inside your project only. Omitted: the project default, the same one
1447
+ * /v1 uses when no `Zavu-Sender` header is sent. Ignored when `conversationId` is
1448
+ * set, and with a test-mode key, which always sends from the WhatsApp sandbox.
1449
+ *
1450
+ * An address that no entry has returns `404 not_found`. An address two of your
1451
+ * senders share (a number whose WhatsApp was connected on one sender and SMS on
1452
+ * another) resolves when `channel` names the transport; otherwise it returns `409
1453
+ * ambiguous_from` with `details.candidates[{address, type, v1SenderId}]`, and in
1454
+ * that case, and only then, `from` also accepts one of those `v1SenderId` values.
1455
+ */
1456
+ from?: string;
1457
+ /**
1458
+ * Reply inside an existing conversation: the message leaves from the sender that
1459
+ * last handled it (`from` on the Conversation), and goes to whoever the
1460
+ * conversation is with, so `to` may be omitted. A `to` that is not the
1461
+ * conversation's contact is refused with `400` (`details.errors` on `to` and
1462
+ * `conversationId`, `details.conversationTo`) instead of silently opening another
1463
+ * conversation. Takes precedence over `from`. When `channel` is omitted,
1464
+ * `routing.reason` is then `conversation`. A conversation of another project
1465
+ * returns `404`; one with no sender recorded returns `409 no_channel`. The 202's
1466
+ * `message.conversationId` is set whenever the conversation the message is filed
1467
+ * in already exists; it is absent only for a recipient's first message, whose
1468
+ * conversation is created after the response. A reply to a conversation that an
1469
+ * inbound email opened reaches the right person but is filed in a separate
1470
+ * conversation keyed by the email address.
1471
+ */
1472
+ conversationId?: string;
1473
+ }
1474
+
1475
+ export interface SendMessageResponse {
1476
+ message: Message;
1477
+ routing: Routing;
1478
+ }
1479
+
1480
+ export interface Template {
1481
+ id: string;
1482
+ /** Template name. For WhatsApp, must match the approved template name in Meta. */
1483
+ name: string;
1484
+ /** Language code. */
1485
+ language: string;
1486
+ /**
1487
+ * Default template body with variables: positional ({{1}}, {{2}}) or named
1488
+ * ({{customer_name}}, {{contact.first_name}}). Templates created in Zavu are
1489
+ * submitted to Meta as positional; templates imported from a WhatsApp Business
1490
+ * Account keep their original format (named or positional). Used when no
1491
+ * channel-specific body is set.
1492
+ */
1493
+ body: string;
1494
+ /** Channel-specific body for SMS messages. Falls back to `body` if not set. */
1495
+ smsBody?: string;
1496
+ /** Channel-specific body for Telegram messages. Falls back to `body` if not set. */
1497
+ telegramBody?: string;
1498
+ /** Channel-specific body for Instagram messages. Falls back to `body` if not set. */
1499
+ instagramBody?: string;
1500
+ /**
1501
+ * The category the template is billed under. For a template linked to WhatsApp
1502
+ * this is the category **Meta currently assigns**, not the one it was submitted
1503
+ * with: Meta can recategorize a template at approval or at any point afterwards,
1504
+ * and that changes what the message costs.
1505
+ *
1506
+ * A recategorization reaches this field through the `template.status_changed`
1507
+ * webhook, which carries `data.category`, and through `POST /v1/templates/sync`,
1508
+ * which reconciles it against Meta. Read one of those rather than assuming the
1509
+ * category you sent is the one in force.
1510
+ *
1511
+ * For a template that was never submitted to WhatsApp, this is the category stored
1512
+ * locally, defaulting to `UTILITY`.
1513
+ */
1514
+ category: WhatsAppCategory;
1515
+ /** @defaultValue `"draft"` */
1516
+ status?: TemplateStatus;
1517
+ /** List of variable names for documentation. */
1518
+ variables?: Array<string>;
1519
+ /** Type of header (text, image, video, document). */
1520
+ headerType?: string;
1521
+ /** Header content (text or media URL). */
1522
+ headerContent?: string;
1523
+ /** Footer text for the template. */
1524
+ footer?: string;
1525
+ /** Template buttons. */
1526
+ buttons?: Array<TemplateButtonsItem>;
1527
+ /** Add 'Do not share this code' disclaimer. Only for AUTHENTICATION templates. */
1528
+ addSecurityRecommendation?: boolean;
1529
+ /** Code expiration time in minutes. Only for AUTHENTICATION templates. */
1530
+ codeExpirationMinutes?: number;
1531
+ /** WhatsApp-specific template information. */
1532
+ whatsapp?: TemplateWhatsapp;
1533
+ createdAt?: string;
1534
+ updatedAt?: string;
1535
+ }
1536
+
1537
+ export type TemplateStatus = 'draft' | 'pending' | 'approved' | 'rejected';
1538
+
1539
+ export interface TemplateButtonsItem {
1540
+ type?: TemplateButtonsItemType;
1541
+ text?: string;
1542
+ url?: string;
1543
+ /**
1544
+ * Sample value used to substitute `{{1}}` in the URL when submitting the template
1545
+ * to Meta for review. Only present for dynamic URL buttons.
1546
+ */
1547
+ example?: string;
1548
+ phoneNumber?: string;
1549
+ /** OTP button type. Required when type is 'otp'. */
1550
+ otpType?: TemplateButtonsItemOtpType;
1551
+ /** Android package name. Required for ONE_TAP buttons. */
1552
+ packageName?: string;
1553
+ /** Android app signature hash. Required for ONE_TAP buttons. */
1554
+ signatureHash?: string;
1555
+ }
1556
+
1557
+ export type TemplateButtonsItemType = 'quick_reply' | 'url' | 'phone' | 'otp' | 'request_contact_info';
1558
+
1559
+ export type TemplateButtonsItemOtpType = 'COPY_CODE' | 'ONE_TAP';
1560
+
1561
+ export interface TemplateWhatsapp {
1562
+ /** WhatsApp template name. */
1563
+ templateName?: string;
1564
+ /** WhatsApp Business Account namespace. */
1565
+ namespace?: string;
1566
+ /** WhatsApp approval status. */
1567
+ status?: string;
1568
+ }
1569
+
1570
+ export interface TemplateCreateRequest {
1571
+ name: string;
1572
+ /** @defaultValue `"en"` */
1573
+ language: string;
1574
+ /** Default template body. Used when no channel-specific body is set. */
1575
+ body: string;
1576
+ /** Channel-specific body for SMS. Falls back to `body` if not set. */
1577
+ smsBody?: string;
1578
+ /** Channel-specific body for Telegram. Falls back to `body` if not set. */
1579
+ telegramBody?: string;
1580
+ /** Channel-specific body for Instagram. Falls back to `body` if not set. */
1581
+ instagramBody?: string;
1582
+ whatsappCategory?: WhatsAppCategory;
1583
+ /** Type of header for the template. */
1584
+ headerType?: TemplateCreateRequestHeaderType;
1585
+ /** Header content (text string or media URL). */
1586
+ headerContent?: string;
1587
+ /** Footer text for the template. */
1588
+ footer?: string;
1589
+ variables?: Array<string>;
1590
+ /** Template buttons (max 3). */
1591
+ buttons?: Array<TemplateCreateRequestButtonsItem>;
1592
+ /** Add 'Do not share this code' disclaimer. Only for AUTHENTICATION templates. */
1593
+ addSecurityRecommendation?: boolean;
1594
+ /** Code expiration time in minutes. Only for AUTHENTICATION templates. */
1595
+ codeExpirationMinutes?: number;
1596
+ }
1597
+
1598
+ export type TemplateCreateRequestHeaderType = 'text' | 'image' | 'video' | 'document';
1599
+
1600
+ export interface TemplateCreateRequestButtonsItem {
1601
+ /**
1602
+ * `request_contact_info` renders a fixed **Share Contact Info** button that asks
1603
+ * the recipient to share their phone number — useful when a contact adopted a
1604
+ * WhatsApp username and you only know their BSUID. It takes no other fields.
1605
+ */
1606
+ type: TemplateCreateRequestButtonsItemType;
1607
+ /**
1608
+ * Button label. Required for every type except `request_contact_info`, whose label
1609
+ * is fixed by WhatsApp.
1610
+ */
1611
+ text?: string;
1612
+ /**
1613
+ * Button destination. Use `{{1}}` exactly once for a dynamic URL (e.g.
1614
+ * `https://example.com/orders/{{1}}`); WhatsApp only accepts the strict `{{1}}`
1615
+ * form. Static URLs must not contain any `{{...}}` placeholder.
1616
+ */
1617
+ url?: string;
1618
+ /**
1619
+ * Sample value Meta uses to review templates with a dynamic URL button.
1620
+ * Substituted into `{{1}}` of the URL when the template is submitted to Meta. Only
1621
+ * meaningful when `url` contains `{{1}}`; ignored for static URLs.
1622
+ */
1623
+ example?: string;
1624
+ phoneNumber?: string;
1625
+ /**
1626
+ * Required when type is 'otp'. COPY_CODE shows copy button, ONE_TAP enables
1627
+ * Android autofill.
1628
+ */
1629
+ otpType?: TemplateCreateRequestButtonsItemOtpType;
1630
+ /** Android package name. Required for ONE_TAP buttons. */
1631
+ packageName?: string;
1632
+ /** Android app signature hash. Required for ONE_TAP buttons. */
1633
+ signatureHash?: string;
1634
+ }
1635
+
1636
+ export type TemplateCreateRequestButtonsItemType =
1637
+ | 'quick_reply'
1638
+ | 'url'
1639
+ | 'phone'
1640
+ | 'otp'
1641
+ | 'request_contact_info';
1642
+
1643
+ export type TemplateCreateRequestButtonsItemOtpType = 'COPY_CODE' | 'ONE_TAP';
1644
+
1645
+ export interface TemplateSubmitRequest {
1646
+ /**
1647
+ * The WhatsApp number whose Business Account receives the template: an address
1648
+ * with a `whatsapp` entry in `GET /v2/channels`.
1649
+ */
1650
+ from: string;
1651
+ category?: WhatsAppCategory;
1652
+ }
1653
+
1654
+ /**
1655
+ * Type of event that triggers the webhook.
1656
+ *
1657
+ * **Message lifecycle events:**
1658
+ * - `message.queued`: Message created and queued for sending. `data.status` =
1659
+ * `queued`
1660
+ * - `message.sent`: Message accepted by the provider. `data.status` = `sent`
1661
+ * - `message.delivered`: Message delivered to recipient. `data.status` =
1662
+ * `delivered`
1663
+ * - `message.read`: Message was read by the recipient (WhatsApp only).
1664
+ * `data.status` = `read`
1665
+ * - `message.failed`: Message failed to send. `data.status` = `failed`
1666
+ *
1667
+ * **Inbound events:**
1668
+ * - `message.inbound`: New message received from a contact. `data.conversationId`,
1669
+ * the inbox thread id (deep-link with
1670
+ * `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`), is sent on
1671
+ * **email** messages only, and is `null` there on the first message of a
1672
+ * brand-new thread. On every other channel it is absent: read it from `GET
1673
+ * /v2/messages/{messageId}`, which always has it, or from `conversation.new` for
1674
+ * a new contact. Reactions are delivered as `message.inbound` with
1675
+ * `messageType='reaction'`. When the contact replied to (quoted) an earlier
1676
+ * message, `data.content` carries the reply context: `replyToMessageId`,
1677
+ * `replyToProviderMessageId`, `replyToFrom`, `replyToText`, and
1678
+ * `replyToMessageType`. `data.providerTimestamp` is the provider's original
1679
+ * receive time in Unix milliseconds (the moment the channel received the message
1680
+ * from the contact — WhatsApp, Telegram, Instagram, Messenger; `null` for SMS
1681
+ * and email). Compare it against the top-level `timestamp` (when Zavu dispatched
1682
+ * the webhook) to detect and ignore delayed deliveries. When the conversation
1683
+ * was opened from a Click-to-WhatsApp ad or post, `data.referral` carries the ad
1684
+ * attribution — including `ctwaClid`, the identifier Meta's Conversions API
1685
+ * needs to credit a conversion back to that ad. WhatsApp only, and only on the
1686
+ * first message of the thread: it is absent from every later message, so persist
1687
+ * it when it arrives.
1688
+ * - `message.unsupported`: Received a message type that is not supported
1689
+ *
1690
+ * **Broadcast events:**
1691
+ * - `broadcast.status_changed`: Broadcast status changed (pending_review,
1692
+ * approved, rejected, sending, completed, cancelled)
1693
+ *
1694
+ * **Other events:**
1695
+ * - `conversation.new`: New conversation started with a contact. `data` carries
1696
+ * `conversationId` (the inbox thread id — deep-link with
1697
+ * `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`), the
1698
+ * `phoneNumber` or `email` key, `channel`, `firstMessageId`, `firstMessageText`,
1699
+ * and `profileName`.
1700
+ * - `template.status_changed`: WhatsApp template approval status changed. `data`
1701
+ * carries `templateId`, `name`, `previousStatus`, `currentStatus`,
1702
+ * `rejectionReason`, and `category` — the category Meta currently bills the
1703
+ * template under. Meta can recategorize a template (typically `UTILITY` to
1704
+ * `MARKETING`) at approval or long afterwards, which changes what each message
1705
+ * costs; `category` is how that reaches you. A recategorization with no status
1706
+ * change is delivered as this same event, so compare `category` against what you
1707
+ * hold rather than only reacting to `currentStatus`.
1708
+ *
1709
+ * **Partner events:**
1710
+ * - `invitation.status_changed`: A partner invitation's stored status changed: to
1711
+ * `in_progress`, `completed`, `failed`, `cancelled`, or back to `pending` when
1712
+ * it is resent from the dashboard. A change to the same status sends nothing,
1713
+ * and expiry is not a stored change, so no event is sent when an invitation
1714
+ * expires. Delivered to the project that created the invitation; a parent
1715
+ * project does not receive its sub-accounts' events. `data` carries
1716
+ * `invitationId`, `clientName`, `clientEmail`, `connectionType` (`whatsapp_waba`
1717
+ * or `messenger`), `previousStatus`, and `currentStatus`. On `completed` it also
1718
+ * carries `connectedAccount` (`channel`, `id`, `name`), the WhatsApp number or
1719
+ * Facebook Page that was linked, and, for WhatsApp, `wabaAccountId`. On `failed`
1720
+ * it carries `failureReason`; the invitation link stays usable, so a client can
1721
+ * retry it.
1722
+ *
1723
+ * **Voice Agent events:**
1724
+ * For every voice event, `data` carries `callId`, `direction`, `from`, `to`,
1725
+ * `status`, `durationSeconds`, `endReason`, and `transcriptAvailable`. The
1726
+ * terminal events (`call.completed`, `call.failed`) additionally carry `cost` —
1727
+ * what the call was billed, in USD, combining telephony and the managed voice
1728
+ * pipeline — and `currency`. They are dispatched after the call is charged, so
1729
+ * `cost` is populated rather than zero; telephony can still be settling on an
1730
+ * outbound call, in which case `GET /v1/calls/{callId}` holds the reconciled
1731
+ * figure.
1732
+ * - `call.initiated`: An outbound call was created and is dialing, or an inbound
1733
+ * call was received. `data.status` = `ringing`
1734
+ * - `call.answered`: The call was answered and the voice agent is connected.
1735
+ * `data.status` = `in_progress`
1736
+ * - `call.completed`: The call ended after a conversation. `data.status` =
1737
+ * `completed`; `durationSeconds` and `endReason` describe how it ended, and
1738
+ * `transcriptAvailable` indicates whether a transcript can be fetched.
1739
+ * - `call.failed`: The call could not be completed (busy, no answer, canceled, or
1740
+ * an error). `data.status` is the terminal status and `endReason` explains the
1741
+ * cause.
1742
+ *
1743
+ * **Custom domain events:**
1744
+ * - `domain.verified`: A custom email domain passed verification (DKIM, and
1745
+ * SPF/DMARC/MAIL FROM if enhanced records are enabled)
1746
+ * - `domain.failed`: A custom email domain failed verification or is partially
1747
+ * verified
1748
+ */
1749
+ export type WebhookEvent =
1750
+ | 'message.queued'
1751
+ | 'message.sent'
1752
+ | 'message.delivered'
1753
+ | 'message.read'
1754
+ | 'message.failed'
1755
+ | 'message.inbound'
1756
+ | 'message.unsupported'
1757
+ | 'broadcast.status_changed'
1758
+ | 'conversation.new'
1759
+ | 'template.status_changed'
1760
+ | 'invitation.status_changed'
1761
+ | 'call.initiated'
1762
+ | 'call.answered'
1763
+ | 'call.completed'
1764
+ | 'call.failed'
1765
+ | 'domain.verified'
1766
+ | 'domain.failed';
1767
+
1768
+ /**
1769
+ * Data payload for message webhook events (queued, sent, delivered, failed,
1770
+ * inbound).
1771
+ */
1772
+ export interface WebhookMessageData {
1773
+ /** ID of the message. */
1774
+ messageId: string;
1775
+ /**
1776
+ * Recipient phone number or email. On `message.inbound` this is your own number
1777
+ * (the message's destination).
1778
+ */
1779
+ to: string;
1780
+ /**
1781
+ * Sender of the message. Present on `message.inbound`: the contact who sent the
1782
+ * message.
1783
+ */
1784
+ from?: string;
1785
+ channel: ChannelType;
1786
+ status: MessageStatus;
1787
+ /** Type of the message. Present on `message.inbound`. */
1788
+ messageType?: MessageType;
1789
+ /**
1790
+ * ID of the conversation (inbox thread) this message belongs to. Sent on
1791
+ * `message.inbound` for **email** messages only; on SMS, WhatsApp, Telegram,
1792
+ * Instagram and Messenger it is absent. Use it to deep-link to the dashboard
1793
+ * inbox: `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`. On
1794
+ * email it is `null` on the first message of a brand-new thread. In every case,
1795
+ * fetch it via `GET /v2/messages/{messageId}`, whose `conversationId` is always
1796
+ * populated, or take it from the `conversation.new` event for a new contact.
1797
+ */
1798
+ conversationId?: string | null;
1799
+ /**
1800
+ * Text body or media caption. Present on `message.inbound` when the message has
1801
+ * text.
1802
+ */
1803
+ text?: string;
1804
+ /** Error code if the message failed. */
1805
+ errorCode?: string | null;
1806
+ /** Error message if the message failed. */
1807
+ errorMessage?: string | null;
1808
+ /**
1809
+ * The provider's original receive time in Unix milliseconds (when the channel
1810
+ * received the message from the contact). Present on `message.inbound` for
1811
+ * channels that report it (WhatsApp, Telegram, Instagram, Messenger); null for SMS
1812
+ * and email.
1813
+ */
1814
+ providerTimestamp?: number | null;
1815
+ /**
1816
+ * Click-to-WhatsApp ad attribution. Present on `message.inbound` only when this is
1817
+ * the first message of a conversation opened from a Meta ad or post — absent on
1818
+ * every later message in that same thread, and on organic conversations. Read
1819
+ * `referral.ctwaClid` here to attribute the conversation, and persist it on your
1820
+ * side: this webhook is the only delivery. `GET /v2/messages/{messageId}` also
1821
+ * returns it, under `content.referral`.
1822
+ */
1823
+ referral?: AdReferral;
1824
+ }
1825
+
1826
+ /**
1827
+ * What a `POST` to your /v2 webhook carries. Signed like every Zavu webhook
1828
+ * (`X-Zavu-Signature`, with the webhook's `signatureVersion`); the event name is
1829
+ * also in the `X-Zavu-Event` header. No sender id appears anywhere in it.
1830
+ */
1831
+ export interface WebhookPayloadV2 {
1832
+ /**
1833
+ * Unique event identifier. Different from the id of the same event delivered to a
1834
+ * /v1 webhook.
1835
+ */
1836
+ id: string;
1837
+ event: WebhookEvent;
1838
+ /** Unix milliseconds when the event was created. */
1839
+ timestamp: number;
1840
+ projectId: string;
1841
+ /**
1842
+ * Where the event's message or call came from. Outbound message events: your
1843
+ * address. `message.inbound`, `message.unsupported`, `conversation.new`: the
1844
+ * contact. `call.*`: the caller. Project-level events (`invitation.*`, `domain.*`,
1845
+ * `broadcast.*`) are not about one address: read `data`. On those, `from`, `to`
1846
+ * and `type` can be null, or name an address of the channel group the event was
1847
+ * recorded on.
1848
+ */
1849
+ from: string | null;
1850
+ /**
1851
+ * The other end: the recipient of an outbound message, your address for an inbound
1852
+ * one, the callee of a call.
1853
+ */
1854
+ to: string | null;
1855
+ /** The transport, as in `GET /v2/channels`. Null when the event names none. */
1856
+ type: ChannelType | null;
1857
+ /** The same event data /v1 sends (see `WebhookEvent`), without `senderId`. */
1858
+ data: { [key: string]: unknown };
1859
+ }
1860
+
1861
+ /**
1862
+ * Which `X-Zavu-Signature` scheme this receiver is sent. The header is `t=<unix
1863
+ * seconds>` followed by the signatures.
1864
+ *
1865
+ * - `v1`: `v1=HMAC_SHA256(secret, body)`.
1866
+ * - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the
1867
+ * default for a new /v2 webhook. It signs the timestamp together with the body.
1868
+ * - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver
1869
+ * reading either one works.
1870
+ *
1871
+ * Moving straight between `v1` and `v2`, in either direction, returns `400`. Set
1872
+ * `v1+v2` first. See
1873
+ * https://docs.zavu.dev/guides/receiving-messages/signature-migration
1874
+ */
1875
+ export type WebhookSignatureVersion = 'v1' | 'v1+v2' | 'v2';
1876
+
1877
+ /** WhatsApp template category. */
1878
+ export type WhatsAppCategory = 'UTILITY' | 'MARKETING' | 'AUTHENTICATION';
1879
+
1880
+ /** One place the project can send from, on one transport. */
1881
+ export interface ChannelEntry {
1882
+ /**
1883
+ * The address the recipient sees: an E.164 number (`sms`, `voice`, `whatsapp`), an
1884
+ * email address (lowercase), `@botname` (`telegram`), `@username` (`instagram`),
1885
+ * the Facebook Page id (`messenger`), or the literal `sms_oneway` (one-way SMS
1886
+ * goes out from shared system numbers, and messages on it store this same literal
1887
+ * as `from`).
1888
+ */
1889
+ address: string;
1890
+ type: ChannelType;
1891
+ /**
1892
+ * It can send right now. A connected transport that is switched off is listed with
1893
+ * `active: false`; turn it on with `POST /v2/channels/{address}/{type}/activate`.
1894
+ * `sms`, `sms_oneway` and `voice` have no switch and are always active.
1895
+ */
1896
+ active: boolean;
1897
+ /**
1898
+ * Read-only. The /v1 sender behind this entry, usable as the `Zavu-Sender` header
1899
+ * on /v1. Entries with the same `v1SenderId` are one channel group. On /v2 it is
1900
+ * accepted as `from` only when two entries of different groups share an address
1901
+ * (see `409 ambiguous_from`).
1902
+ */
1903
+ v1SenderId: string;
1904
+ }
1905
+
1906
+ /**
1907
+ * A /v1 sender webhook that is still on. Each one also receives the events it is
1908
+ * subscribed to, as a separate delivery.
1909
+ */
1910
+ export interface SenderWebhookV1 {
1911
+ /** The sender's first address; null for a sender with no connected transport. */
1912
+ address: string | null;
1913
+ url: string;
1914
+ /** Read-only; the sender to change on /v1. */
1915
+ v1SenderId: string;
1916
+ }
1917
+
1918
+ /**
1919
+ * What to connect. Only channels that need no purchase and no third-party signup
1920
+ * are connected here: one-way SMS, an email address at a verified domain, a
1921
+ * Telegram bot. Two-way SMS and voice come with a number (`POST /v2/numbers`);
1922
+ * WhatsApp, Instagram and Messenger connect through Meta, from Accounts in the
1923
+ * dashboard. Any of those types here is a `400` saying so.
1924
+ */
1925
+ export type ChannelConnectRequest =
1926
+ | ChannelConnectRequestSmsOneway
1927
+ | ChannelConnectRequestEmail
1928
+ | ChannelConnectRequestTelegram;
1929
+
1930
+ export interface EmailDomain {
1931
+ id: string;
1932
+ domain: string;
1933
+ /** Overall verification status. */
1934
+ status: string;
1935
+ dkimStatus: string;
1936
+ /**
1937
+ * DNS records to publish. Present when fetching a single domain or after adding
1938
+ * one.
1939
+ */
1940
+ dnsRecords?: Array<EmailDomainDnsRecord>;
1941
+ /**
1942
+ * Present with `dnsRecords`. False when the DKIM records could not be generated:
1943
+ * such a domain cannot verify whatever DNS you publish, and `setupError` says what
1944
+ * to do.
1945
+ */
1946
+ verifiable?: boolean;
1947
+ /**
1948
+ * Present when `verifiable` is false. `recommendation` is the same everywhere this
1949
+ * domain is refused (`POST .../verify`, `POST /v2/channels`): remove the domain
1950
+ * and add it again, which sets it up from scratch; contact support if the new one
1951
+ * has no `dkim` record either. It never says to retry verify or to publish the
1952
+ * other records.
1953
+ */
1954
+ setupError?: EmailDomainSetupError;
1955
+ }
1956
+
1957
+ export interface EmailDomainSetupError {
1958
+ code?: 'dkim_records_unavailable';
1959
+ message?: string;
1960
+ recommendation?: string;
1961
+ }
1962
+
1963
+ export interface EmailDomainDnsRecord {
1964
+ /** DNS record type. */
1965
+ type: string;
1966
+ /** Record host/name to create. */
1967
+ name: string;
1968
+ /** Record value. */
1969
+ value: string;
1970
+ /** Priority (MX records only). */
1971
+ priority?: number;
1972
+ /** What the record is for. */
1973
+ purpose: EmailDomainDnsRecordPurpose;
1974
+ /**
1975
+ * Whether the record is required to verify + send (DKIM) or recommended for
1976
+ * deliverability.
1977
+ */
1978
+ required: boolean;
1979
+ }
1980
+
1981
+ export type EmailDomainDnsRecordPurpose = 'dkim' | 'spf' | 'dmarc' | 'mail_from';
1982
+
1983
+ /**
1984
+ * A Zavu Function: TypeScript that runs in Zavu Cloud, either reacting to events,
1985
+ * serving HTTP, or holding the agents its code declares.
1986
+ */
1987
+ export interface ZavuFunction {
1988
+ id: string;
1989
+ slug: string;
1990
+ name: string;
1991
+ description?: string | null;
1992
+ runtime: string;
1993
+ status: FunctionStatus;
1994
+ /**
1995
+ * True only while a version is live and not paused (`status: active`). A draft, a
1996
+ * failed first deploy, a deploy in progress or a pause are all `false`.
1997
+ */
1998
+ enabled: boolean;
1999
+ timeoutSec: number;
2000
+ memoryMb: FunctionMemoryMb;
2001
+ /**
2002
+ * arm64 is the default and the cheaper one; x86_64 bills each invocation at 1.25x
2003
+ * units. Applied on the next deploy.
2004
+ */
2005
+ architecture: FunctionArchitecture;
2006
+ httpEnabled: boolean;
2007
+ /** Null while `httpEnabled` is false: the URL does not serve then. */
2008
+ publicUrl?: string | null;
2009
+ activeDeploymentId?: string | null;
2010
+ createdAt: string;
2011
+ updatedAt: string;
2012
+ /**
2013
+ * The customer paused it. A deploy, a git push or a rollback does not resume it.
2014
+ * Pausing stops event triggers; the HTTP endpoint has its own switch,
2015
+ * `httpEnabled`, and the tools of the agents it declares keep answering.
2016
+ */
2017
+ paused: boolean;
2018
+ }
2019
+
2020
+ export type FunctionStatus = 'draft' | 'deploying' | 'active' | 'failed' | 'disabled';
2021
+
2022
+ export type FunctionMemoryMb = 128 | 256 | 512 | 1024;
2023
+
2024
+ export type FunctionArchitecture = 'arm64' | 'x86_64';
2025
+
2026
+ /**
2027
+ * Configuration only, applied in one transaction: if any part is refused, nothing
2028
+ * changes. Code, dependencies and deploys are on /v1 (`PATCH
2029
+ * /v1/functions/{functionId}` and `POST /v1/functions/{functionId}/deploy`). Send
2030
+ * at least one field. Every invalid field is reported in `details.errors`.
2031
+ */
2032
+ export interface FunctionUpdateRequest {
2033
+ name?: string;
2034
+ /** null clears it. */
2035
+ description?: string | null;
2036
+ /** Applied on the next deploy. */
2037
+ timeoutSec?: number;
2038
+ /** Applied on the next deploy. */
2039
+ memoryMb?: FunctionUpdateRequestMemoryMb;
2040
+ architecture?: FunctionUpdateRequestArchitecture;
2041
+ /**
2042
+ * false pauses the function, true resumes it. Refused with 409
2043
+ * `function_not_ready` while it is a draft, deploying, or its first deploy failed.
2044
+ */
2045
+ enabled?: boolean;
2046
+ /**
2047
+ * Switch the public endpoint on or off. Applies to the deployed function without a
2048
+ * redeploy.
2049
+ */
2050
+ httpEnabled?: boolean;
2051
+ }
2052
+
2053
+ export type FunctionUpdateRequestMemoryMb = 128 | 256 | 512 | 1024;
2054
+
2055
+ export type FunctionUpdateRequestArchitecture = 'arm64' | 'x86_64';
2056
+
2057
+ /**
2058
+ * One-way SMS from shared system numbers. No number or credential; recipients
2059
+ * cannot reply. Sends within the SMS daily ceiling. Sending to a US or Canadian
2060
+ * (+1) number needs an approved 10DLC campaign first, and is refused with `403
2061
+ * ten_dlc_required` until then. Refused with `403 kyb_required` only on an account
2062
+ * that must verify its business before using one-way SMS.
2063
+ */
2064
+ export interface ChannelConnectRequestSmsOneway {
2065
+ type: 'sms_oneway';
2066
+ }
2067
+
2068
+ /**
2069
+ * An address at an email domain of this project that is verified (`GET
2070
+ * /v2/email-domains`).
2071
+ */
2072
+ export interface ChannelConnectRequestEmail {
2073
+ type: 'email';
2074
+ address: string;
2075
+ /** Display name in the recipient's inbox. */
2076
+ fromName?: string;
2077
+ }
2078
+
2079
+ /** A Telegram bot. Its address is `@botname`. */
2080
+ export interface ChannelConnectRequestTelegram {
2081
+ type: 'telegram';
2082
+ /** Bot token from @BotFather. */
2083
+ botToken: string;
2084
+ }
2085
+
2086
+ export type ListMessagesV2Status = 'queued' | 'sending' | 'sent' | 'delivered' | 'failed' | 'received';
2087
+
2088
+ export interface ListMessagesV2Response {
2089
+ items: Array<Message>;
2090
+ nextCursor?: string | null;
2091
+ }
2092
+
2093
+ export interface GetMessageV2Response {
2094
+ message: Message;
2095
+ }
2096
+
2097
+ export interface ListConversationsV2Response {
2098
+ items: Array<Conversation>;
2099
+ nextCursor?: string | null;
2100
+ }
2101
+
2102
+ export interface GetConversationV2Response {
2103
+ conversation: Conversation;
2104
+ }
2105
+
2106
+ export interface ListConversationMessagesV2Response {
2107
+ items: Array<Message>;
2108
+ nextCursor?: string | null;
2109
+ }
2110
+
2111
+ export interface MarkConversationReadV2Response {
2112
+ conversation: Conversation;
2113
+ }
2114
+
2115
+ export interface ListContactsV2Response {
2116
+ items: Array<Contact>;
2117
+ nextCursor?: string | null;
2118
+ }
2119
+
2120
+ export interface ListChannelsV2Response {
2121
+ items: Array<ChannelEntry>;
2122
+ nextCursor?: string | null;
2123
+ }
2124
+
2125
+ export interface ConnectChannelV2Response {
2126
+ items: Array<ChannelEntry>;
2127
+ }
2128
+
2129
+ export interface ListTemplatesV2Response {
2130
+ items: Array<Template>;
2131
+ nextCursor?: string | null;
2132
+ }
2133
+
2134
+ export interface ListCampaignsV2Response {
2135
+ items: Array<Broadcast>;
2136
+ nextCursor: string | null;
2137
+ }
2138
+
2139
+ export interface CreateCampaignV2Response {
2140
+ campaign: Broadcast;
2141
+ }
2142
+
2143
+ export interface GetCampaignV2Response {
2144
+ campaign: Broadcast;
2145
+ }
2146
+
2147
+ export interface SendCampaignV2Response {
2148
+ campaign: Broadcast;
2149
+ }
2150
+
2151
+ export interface GetWebhookV2Response {
2152
+ webhook: ProjectWebhookV2 | null;
2153
+ senderWebhooks: Array<SenderWebhookV1>;
2154
+ }
2155
+
2156
+ export interface PutWebhookV2Response {
2157
+ webhook: ProjectWebhookV2;
2158
+ senderWebhooks: Array<SenderWebhookV1>;
2159
+ /** How many /v1 sender webhooks this call switched off. */
2160
+ disabledSenderWebhooks: number;
2161
+ }
2162
+
2163
+ export interface RotateWebhookSecretV2Response {
2164
+ secret: string;
2165
+ }
2166
+
2167
+ export interface ListAgentsV2Response {
2168
+ items: Array<Agent>;
2169
+ nextCursor?: string | null;
2170
+ }
2171
+
2172
+ export interface CreateAgentV2Response {
2173
+ agent: Agent;
2174
+ }
2175
+
2176
+ export interface GetAgentV2Response {
2177
+ agent: Agent;
2178
+ }
2179
+
2180
+ export interface UpdateAgentV2Response {
2181
+ agent: Agent;
2182
+ }
2183
+
2184
+ export interface GetChannelV2Response {
2185
+ items: Array<ChannelEntry>;
2186
+ }
2187
+
2188
+ export interface ActivateChannelV2Response {
2189
+ address: string;
2190
+ type: ActivatableChannel;
2191
+ /** Every entry of the address after the change. */
2192
+ channels: Array<ChannelEntry>;
2193
+ activated: boolean;
2194
+ chargedCents: number;
2195
+ monthlyCents: number;
2196
+ }
2197
+
2198
+ export interface DeactivateChannelV2Response {
2199
+ address: string;
2200
+ type: ActivatableChannel;
2201
+ /** Every entry of the address after the change. */
2202
+ channels: Array<ChannelEntry>;
2203
+ deactivated: boolean;
2204
+ }
2205
+
2206
+ export interface SearchNumbersV2Response {
2207
+ items: Array<AvailablePhoneNumber>;
2208
+ }
2209
+
2210
+ export interface PurchaseNumberV2Response {
2211
+ number: OwnedPhoneNumber;
2212
+ /** The entries of the new number (`sms`, once routed). */
2213
+ channels: Array<ChannelEntry>;
2214
+ /**
2215
+ * Present only when the purchase succeeded and turning SMS on did not.
2216
+ * `details.recommendation` says how to retry.
2217
+ */
2218
+ setupError?: ErrorBody;
2219
+ }
2220
+
2221
+ export interface ConnectAgentChannelV2Body {
2222
+ address: string;
2223
+ type: ChannelType;
2224
+ }
2225
+
2226
+ export interface ConnectAgentChannelV2Response {
2227
+ agent: Agent;
2228
+ }
2229
+
2230
+ export interface ListEmailDomainsV2Response {
2231
+ items: Array<EmailDomain>;
2232
+ }
2233
+
2234
+ export interface AddEmailDomainV2Body {
2235
+ domain: string;
2236
+ }
2237
+
2238
+ export interface AddEmailDomainV2Response {
2239
+ domain: EmailDomain;
2240
+ }
2241
+
2242
+ export interface GetEmailDomainV2Response {
2243
+ domain: EmailDomain;
2244
+ }
2245
+
2246
+ export interface VerifyEmailDomainV2Response {
2247
+ domain: EmailDomain;
2248
+ }
2249
+
2250
+ export interface CancelCampaignV2Response {
2251
+ campaign: Broadcast;
2252
+ }
2253
+
2254
+ export interface ListFunctionsV2Response {
2255
+ items: Array<ZavuFunction>;
2256
+ nextCursor: string | null;
2257
+ }
2258
+
2259
+ export interface GetFunctionV2Response {
2260
+ function: ZavuFunction;
2261
+ }
2262
+
2263
+ export interface UpdateFunctionV2Response {
2264
+ function: ZavuFunction;
2265
+ }
2266
+
2267
+ /** Params for `messages.send`. */
2268
+ export interface MessagesSendParams extends SendMessageRequest {}
2269
+
2270
+ /** Params for `messages.list`. */
2271
+ export interface MessagesListParams {
2272
+ status?: ListMessagesV2Status;
2273
+ to?: string;
2274
+ channel?: ChannelType;
2275
+ /** @defaultValue `50` */
2276
+ limit?: number;
2277
+ /** Opaque `nextCursor` from the previous page. */
2278
+ cursor?: string;
2279
+ }
2280
+
2281
+ /** Params for `conversations.list`. */
2282
+ export interface ConversationsListParams {
2283
+ /** Only threads that have carried this transport. */
2284
+ channel?: ChannelType;
2285
+ /**
2286
+ * Keep only threads last handled from this address. Resolved like `from` on `POST
2287
+ * /v2/messages`, narrowed by `channel` when given.
2288
+ */
2289
+ from?: string;
2290
+ /** Identity search (number, email, username). Never message bodies. */
2291
+ search?: string;
2292
+ /** @defaultValue `50` */
2293
+ limit?: number;
2294
+ /** Opaque `nextCursor` from the previous page. */
2295
+ cursor?: string;
2296
+ }
2297
+
2298
+ /** Params for `conversations.listMessages`. */
2299
+ export interface ConversationsListMessagesParams {
2300
+ /** @defaultValue `50` */
2301
+ limit?: number;
2302
+ /** Opaque `nextCursor` from the previous page. */
2303
+ cursor?: string;
2304
+ }
2305
+
2306
+ /** Params for `contacts.list`. */
2307
+ export interface ContactsListParams {
2308
+ search?: string;
2309
+ tag?: Array<string>;
2310
+ /** @defaultValue `50` */
2311
+ limit?: number;
2312
+ /** Opaque `nextCursor` from the previous page. */
2313
+ cursor?: string;
2314
+ }
2315
+
2316
+ /** Params for `contacts.create`. */
2317
+ export interface ContactsCreateParams extends ContactCreateRequest {}
2318
+
2319
+ /** Params for `contacts.update`. */
2320
+ export interface ContactsUpdateParams extends ContactUpdateRequest {}
2321
+
2322
+ /** Params for `channels.list`. */
2323
+ export interface ChannelsListParams {
2324
+ /** @defaultValue `50` */
2325
+ limit?: number;
2326
+ /** Opaque `nextCursor` from the previous page. */
2327
+ cursor?: string;
2328
+ }
2329
+
2330
+ /** Params for `channels.connect`. */
2331
+ export type ChannelsConnectParams = ChannelConnectRequest;
2332
+
2333
+ /** Params for `templates.list`. */
2334
+ export interface TemplatesListParams {
2335
+ /** @defaultValue `50` */
2336
+ limit?: number;
2337
+ /** Opaque `nextCursor` from the previous page. */
2338
+ cursor?: string;
2339
+ }
2340
+
2341
+ /** Params for `templates.create`. */
2342
+ export interface TemplatesCreateParams extends TemplateCreateRequest {}
2343
+
2344
+ /** Params for `templates.submit`. */
2345
+ export interface TemplatesSubmitParams extends TemplateSubmitRequest {}
2346
+
2347
+ /** Params for `campaigns.list`. */
2348
+ export interface CampaignsListParams {
2349
+ status?: BroadcastStatus;
2350
+ /** @defaultValue `50` */
2351
+ limit?: number;
2352
+ /** Opaque, from a previous `nextCursor`. */
2353
+ cursor?: string;
2354
+ }
2355
+
2356
+ /** Params for `campaigns.create`. */
2357
+ export interface CampaignsCreateParams extends BroadcastCreateRequest {}
2358
+
2359
+ /** Params for `campaigns.send`. */
2360
+ export interface CampaignsSendParams extends BroadcastSendRequest {}
2361
+
2362
+ /** Params for `campaigns.recipients.add`. */
2363
+ export interface CampaignsRecipientsAddParams extends BroadcastContactsAddRequest {}
2364
+
2365
+ /** Params for `webhook.set`. */
2366
+ export interface WebhookSetParams extends ProjectWebhookV2Put {}
2367
+
2368
+ /** Params for `agents.list`. */
2369
+ export interface AgentsListParams {
2370
+ /** @defaultValue `50` */
2371
+ limit?: number;
2372
+ /** Opaque `nextCursor` from the previous page. */
2373
+ cursor?: string;
2374
+ }
2375
+
2376
+ /** Params for `agents.create`. */
2377
+ export interface AgentsCreateParams extends AgentCreateStandaloneRequest {}
2378
+
2379
+ /** Params for `agents.update`. */
2380
+ export interface AgentsUpdateParams extends AgentUpdateRequest {}
2381
+
2382
+ /** Params for `agents.test`. */
2383
+ export interface AgentsTestParams extends AgentTestRequest {}
2384
+
2385
+ /** Params for `agents.channels.connect`. */
2386
+ export interface AgentsChannelsConnectParams extends ConnectAgentChannelV2Body {}
2387
+
2388
+ /** Params for `numbers.searchAvailable`. */
2389
+ export interface NumbersSearchAvailableParams {
2390
+ countryCode: string;
2391
+ type?: PhoneNumberType;
2392
+ contains?: string;
2393
+ capabilities?: string;
2394
+ /** @defaultValue `10` */
2395
+ limit?: number;
2396
+ }
2397
+
2398
+ /** Params for `numbers.purchase`. */
2399
+ export interface NumbersPurchaseParams extends PhoneNumberPurchaseRequest {}
2400
+
2401
+ /** Params for `emailDomains.create`. */
2402
+ export interface EmailDomainsCreateParams extends AddEmailDomainV2Body {}
2403
+
2404
+ /** Params for `functions.list`. */
2405
+ export interface FunctionsListParams {
2406
+ /** @defaultValue `50` */
2407
+ limit?: number;
2408
+ cursor?: string;
2409
+ }
2410
+
2411
+ /** Params for `functions.update`. */
2412
+ export interface FunctionsUpdateParams extends FunctionUpdateRequest {}