@zyphr-dev/node-sdk 0.1.54 → 0.1.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyphr-dev/node-sdk",
3
- "version": "0.1.54",
3
+ "version": "0.1.56",
4
4
  "description": "Official Zyphr SDK for Node.js, React, and React Native",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -523,9 +523,6 @@ src/models/WebhookIpsResponseData.ts
523
523
  src/models/WebhookJobResponse.ts
524
524
  src/models/WebhookListResponse.ts
525
525
  src/models/WebhookMetricsResponse.ts
526
- src/models/WebhookRegion.ts
527
- src/models/WebhookRegionsResponse.ts
528
- src/models/WebhookRegionsResponseData.ts
529
526
  src/models/WebhookReplayResponse.ts
530
527
  src/models/WebhookResponse.ts
531
528
  src/models/WebhookSecretRotateResponse.ts
package/src/client.ts CHANGED
@@ -34,6 +34,7 @@ import {
34
34
  DevicesApi,
35
35
  DomainsApi,
36
36
  UtilityApi,
37
+ WorkflowsApi,
37
38
  EmailsApi,
38
39
  InboundEmailApi,
39
40
  InboxApi,
@@ -103,6 +104,15 @@ export class Zyphr {
103
104
  /** Publishable-key-safe utilities: password policy + pre-submit strength check. */
104
105
  readonly utility: UtilityApi;
105
106
  readonly domains: DomainsApi;
107
+ /**
108
+ * Multi-step workflow definitions and triggering (sc-7926).
109
+ *
110
+ * All 12 operations are API-key authenticated with dedicated
111
+ * `workflows:read` / `workflows:write` / `workflows:execute` scopes, so this
112
+ * is deliberate public surface. It was generated but never wired onto the
113
+ * facade, which made the documented `zyphr.workflows.*` calls throw.
114
+ */
115
+ readonly workflows: WorkflowsApi;
106
116
  /** Inbound (received) email — read access. Requires the `email:read` scope. */
107
117
  readonly inbound: InboundEmailApi;
108
118
 
@@ -162,6 +172,7 @@ export class Zyphr {
162
172
  this.devices = new DevicesApi(config);
163
173
  this.utility = new UtilityApi(config);
164
174
  this.domains = new DomainsApi(config);
175
+ this.workflows = new WorkflowsApi(config);
165
176
  this.inbound = new InboundEmailApi(config);
166
177
 
167
178
  this.waas = {
@@ -28,6 +28,11 @@ import {
28
28
  SetSelfApplicationTestRecipientsRequestToJSON,
29
29
  } from '../models/index';
30
30
 
31
+ export interface AuthApplicationApiExportEndUserDataRequest {
32
+ id: string;
33
+ userId: string;
34
+ }
35
+
31
36
  export interface AuthApplicationApiSetSelfApplicationTestRecipientsOperationRequest {
32
37
  setSelfApplicationTestRecipientsRequest: SetSelfApplicationTestRecipientsRequest;
33
38
  }
@@ -39,6 +44,23 @@ export interface AuthApplicationApiSetSelfApplicationTestRecipientsOperationRequ
39
44
  * @interface AuthApplicationApiInterface
40
45
  */
41
46
  export interface AuthApplicationApiInterface {
47
+ /**
48
+ * Returns a machine-readable JSON bundle of everything stored about one end user, for answering a GDPR Art. 15 (access) or Art. 20 (portability) request. Zyphr is a **processor**: we do not answer data subjects directly. This endpoint gives you, the controller, the data needed to respond to your own end user. **Credentials are deliberately excluded** and are listed in `export_metadata.excluded`: password hashes, session refresh-token hashes, MFA secrets, OAuth access/refresh tokens and WebAuthn public keys. These are authentication artifacts rather than personal data about the subject, and including them would turn this endpoint into a credential-exfiltration path. Message history is capped at the 1,000 most recent messages; `export_metadata.message_history_truncated` reports whether the cap was hit.
49
+ * @summary Export all data held about an end user
50
+ * @param {string} id Application ID
51
+ * @param {string} userId End user ID
52
+ * @param {*} [options] Override http request option.
53
+ * @throws {RequiredError}
54
+ * @memberof AuthApplicationApiInterface
55
+ */
56
+ exportEndUserDataRaw(requestParameters: AuthApplicationApiExportEndUserDataRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>>;
57
+
58
+ /**
59
+ * Returns a machine-readable JSON bundle of everything stored about one end user, for answering a GDPR Art. 15 (access) or Art. 20 (portability) request. Zyphr is a **processor**: we do not answer data subjects directly. This endpoint gives you, the controller, the data needed to respond to your own end user. **Credentials are deliberately excluded** and are listed in `export_metadata.excluded`: password hashes, session refresh-token hashes, MFA secrets, OAuth access/refresh tokens and WebAuthn public keys. These are authentication artifacts rather than personal data about the subject, and including them would turn this endpoint into a credential-exfiltration path. Message history is capped at the 1,000 most recent messages; `export_metadata.message_history_truncated` reports whether the cap was hit.
60
+ * Export all data held about an end user
61
+ */
62
+ exportEndUserData(id: string, userId: string, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void>;
63
+
42
64
  /**
43
65
  * Return non-sensitive metadata about the application whose credentials authenticated this request. Lets an integrator read their own application_id (the sole tenant discriminator embedded in every end-user token) directly from the API, rather than only from the dashboard UI or by decoding a token. Returns only id, name, the calling environment\'s mode, and the JWT signing algorithm — never secrets, signing keys, or account-internal fields.
44
66
  * @summary Get the authenticating application
@@ -77,6 +99,47 @@ export interface AuthApplicationApiInterface {
77
99
  */
78
100
  export class AuthApplicationApi extends runtime.BaseAPI implements AuthApplicationApiInterface {
79
101
 
102
+ /**
103
+ * Returns a machine-readable JSON bundle of everything stored about one end user, for answering a GDPR Art. 15 (access) or Art. 20 (portability) request. Zyphr is a **processor**: we do not answer data subjects directly. This endpoint gives you, the controller, the data needed to respond to your own end user. **Credentials are deliberately excluded** and are listed in `export_metadata.excluded`: password hashes, session refresh-token hashes, MFA secrets, OAuth access/refresh tokens and WebAuthn public keys. These are authentication artifacts rather than personal data about the subject, and including them would turn this endpoint into a credential-exfiltration path. Message history is capped at the 1,000 most recent messages; `export_metadata.message_history_truncated` reports whether the cap was hit.
104
+ * Export all data held about an end user
105
+ */
106
+ async exportEndUserDataRaw(requestParameters: AuthApplicationApiExportEndUserDataRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
107
+ if (requestParameters['id'] == null) {
108
+ throw new runtime.RequiredError(
109
+ 'id',
110
+ 'Required parameter "id" was null or undefined when calling exportEndUserData().'
111
+ );
112
+ }
113
+
114
+ if (requestParameters['userId'] == null) {
115
+ throw new runtime.RequiredError(
116
+ 'userId',
117
+ 'Required parameter "userId" was null or undefined when calling exportEndUserData().'
118
+ );
119
+ }
120
+
121
+ const queryParameters: any = {};
122
+
123
+ const headerParameters: runtime.HTTPHeaders = {};
124
+
125
+ const response = await this.request({
126
+ path: `/v1/applications/{id}/users/{userId}/export`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))).replace(`{${"userId"}}`, encodeURIComponent(String(requestParameters['userId']))),
127
+ method: 'GET',
128
+ headers: headerParameters,
129
+ query: queryParameters,
130
+ }, initOverrides);
131
+
132
+ return new runtime.VoidApiResponse(response);
133
+ }
134
+
135
+ /**
136
+ * Returns a machine-readable JSON bundle of everything stored about one end user, for answering a GDPR Art. 15 (access) or Art. 20 (portability) request. Zyphr is a **processor**: we do not answer data subjects directly. This endpoint gives you, the controller, the data needed to respond to your own end user. **Credentials are deliberately excluded** and are listed in `export_metadata.excluded`: password hashes, session refresh-token hashes, MFA secrets, OAuth access/refresh tokens and WebAuthn public keys. These are authentication artifacts rather than personal data about the subject, and including them would turn this endpoint into a credential-exfiltration path. Message history is capped at the 1,000 most recent messages; `export_metadata.message_history_truncated` reports whether the cap was hit.
137
+ * Export all data held about an end user
138
+ */
139
+ async exportEndUserData(id: string, userId: string, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
140
+ await this.exportEndUserDataRaw({ id: id, userId: userId }, initOverrides);
141
+ }
142
+
80
143
  /**
81
144
  * Return non-sensitive metadata about the application whose credentials authenticated this request. Lets an integrator read their own application_id (the sole tenant discriminator embedded in every end-user token) directly from the API, rather than only from the dashboard UI or by decoding a token. Returns only id, name, the calling environment\'s mode, and the JWT signing algorithm — never secrets, signing keys, or account-internal fields.
82
145
  * Get the authenticating application
@@ -60,7 +60,7 @@ export interface AuthPasswordResetApiValidateResetTokenOperationRequest {
60
60
  */
61
61
  export interface AuthPasswordResetApiInterface {
62
62
  /**
63
- * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration.
63
+ * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration — the response is identical whether or not the address is registered. An identity with **no password set** still receives a link, which sets a first password. This covers users migrated from a provider that cannot export password hashes. The one case where no email is sent (beyond an unknown address) is an identity that has no password but *does* have another way in — a linked OAuth provider and/or a passkey. Those users are not locked out, so no set-password link is sent; the attempt is recorded as a suppressed message visible via `GET /emails`.
64
64
  * @summary Request a password reset
65
65
  * @param {ForgotPasswordRequest} forgotPasswordRequest
66
66
  * @param {*} [options] Override http request option.
@@ -70,7 +70,7 @@ export interface AuthPasswordResetApiInterface {
70
70
  forgotPasswordRaw(requestParameters: AuthPasswordResetApiForgotPasswordOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SuccessResult>>;
71
71
 
72
72
  /**
73
- * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration.
73
+ * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration — the response is identical whether or not the address is registered. An identity with **no password set** still receives a link, which sets a first password. This covers users migrated from a provider that cannot export password hashes. The one case where no email is sent (beyond an unknown address) is an identity that has no password but *does* have another way in — a linked OAuth provider and/or a passkey. Those users are not locked out, so no set-password link is sent; the attempt is recorded as a suppressed message visible via `GET /emails`.
74
74
  * Request a password reset
75
75
  */
76
76
  forgotPassword(forgotPasswordRequest: ForgotPasswordRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SuccessResult>;
@@ -115,7 +115,7 @@ export interface AuthPasswordResetApiInterface {
115
115
  export class AuthPasswordResetApi extends runtime.BaseAPI implements AuthPasswordResetApiInterface {
116
116
 
117
117
  /**
118
- * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration.
118
+ * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration — the response is identical whether or not the address is registered. An identity with **no password set** still receives a link, which sets a first password. This covers users migrated from a provider that cannot export password hashes. The one case where no email is sent (beyond an unknown address) is an identity that has no password but *does* have another way in — a linked OAuth provider and/or a passkey. Those users are not locked out, so no set-password link is sent; the attempt is recorded as a suppressed message visible via `GET /emails`.
119
119
  * Request a password reset
120
120
  */
121
121
  async forgotPasswordRaw(requestParameters: AuthPasswordResetApiForgotPasswordOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SuccessResult>> {
@@ -152,7 +152,7 @@ export class AuthPasswordResetApi extends runtime.BaseAPI implements AuthPasswor
152
152
  }
153
153
 
154
154
  /**
155
- * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration.
155
+ * Request a password reset email. Creates a reset token and sends an email to the user. Always returns success to prevent email enumeration — the response is identical whether or not the address is registered. An identity with **no password set** still receives a link, which sets a first password. This covers users migrated from a provider that cannot export password hashes. The one case where no email is sent (beyond an unknown address) is an identity that has no password but *does* have another way in — a linked OAuth provider and/or a passkey. Those users are not locked out, so no set-password link is sent; the attempt is recorded as a suppressed message visible via `GET /emails`.
156
156
  * Request a password reset
157
157
  */
158
158
  async forgotPassword(forgotPasswordRequest: ForgotPasswordRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SuccessResult> {
@@ -167,7 +167,7 @@ export interface EmailsApiInterface {
167
167
  sendBatchEmail(sendBatchEmailRequest: SendBatchEmailRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SendBatchEmailResponse>;
168
168
 
169
169
  /**
170
- * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`.
170
+ * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`. **CAN-SPAM compliance — actions required from you:** - **Set `classification`** to `transactional` or `marketing`. Unclassified is treated as marketing, the stricter reading. Transactional mail (password resets, receipts) is exempt from the opt-out and postal-address requirements. - **Set your postal address** in project settings. Commercial email must carry a valid physical postal address — the most commonly missed CAN-SPAM element. Marketing sends without one are flagged. - **Include an unsubscribe mechanism** on commercial email. Zyphr enforces suppression automatically once someone opts out, but the link itself is yours to include. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
171
171
  * @summary Send an email
172
172
  * @param {SendEmailRequest} sendEmailRequest
173
173
  * @param {*} [options] Override http request option.
@@ -177,7 +177,7 @@ export interface EmailsApiInterface {
177
177
  sendEmailRaw(requestParameters: EmailsApiSendEmailOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SendEmailResponse>>;
178
178
 
179
179
  /**
180
- * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`.
180
+ * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`. **CAN-SPAM compliance — actions required from you:** - **Set `classification`** to `transactional` or `marketing`. Unclassified is treated as marketing, the stricter reading. Transactional mail (password resets, receipts) is exempt from the opt-out and postal-address requirements. - **Set your postal address** in project settings. Commercial email must carry a valid physical postal address — the most commonly missed CAN-SPAM element. Marketing sends without one are flagged. - **Include an unsubscribe mechanism** on commercial email. Zyphr enforces suppression automatically once someone opts out, but the link itself is yours to include. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
181
181
  * Send an email
182
182
  */
183
183
  sendEmail(sendEmailRequest: SendEmailRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SendEmailResponse>;
@@ -397,7 +397,7 @@ export class EmailsApi extends runtime.BaseAPI implements EmailsApiInterface {
397
397
  }
398
398
 
399
399
  /**
400
- * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`.
400
+ * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`. **CAN-SPAM compliance — actions required from you:** - **Set `classification`** to `transactional` or `marketing`. Unclassified is treated as marketing, the stricter reading. Transactional mail (password resets, receipts) is exempt from the opt-out and postal-address requirements. - **Set your postal address** in project settings. Commercial email must carry a valid physical postal address — the most commonly missed CAN-SPAM element. Marketing sends without one are flagged. - **Include an unsubscribe mechanism** on commercial email. Zyphr enforces suppression automatically once someone opts out, but the link itself is yours to include. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
401
401
  * Send an email
402
402
  */
403
403
  async sendEmailRaw(requestParameters: EmailsApiSendEmailOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SendEmailResponse>> {
@@ -430,7 +430,7 @@ export class EmailsApi extends runtime.BaseAPI implements EmailsApiInterface {
430
430
  }
431
431
 
432
432
  /**
433
- * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`.
433
+ * Queue an email for delivery. The email is processed asynchronously and you\'ll receive a message ID to track its status. You can provide content directly via `html`/`text` fields, or use a template by specifying `template_id` and `template_data`. **CAN-SPAM compliance — actions required from you:** - **Set `classification`** to `transactional` or `marketing`. Unclassified is treated as marketing, the stricter reading. Transactional mail (password resets, receipts) is exempt from the opt-out and postal-address requirements. - **Set your postal address** in project settings. Commercial email must carry a valid physical postal address — the most commonly missed CAN-SPAM element. Marketing sends without one are flagged. - **Include an unsubscribe mechanism** on commercial email. Zyphr enforces suppression automatically once someone opts out, but the link itself is yours to include. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
434
434
  * Send an email
435
435
  */
436
436
  async sendEmail(sendEmailRequest: SendEmailRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SendEmailResponse> {
@@ -141,6 +141,21 @@ export interface SMSApiInterface {
141
141
  */
142
142
  getSmsConfig(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SmsConfigResponse>;
143
143
 
144
+ /**
145
+ * Returns the URL to configure in your SMS provider so STOP/opt-out replies reach Zyphr. The token is generated on first request and is stable thereafter. **This is required for TCPA compliance.** Until your provider is pointed at this URL, opt-out replies stop at the provider and never reach us — from Zyphr\'s side the recipient never opted out, and sends continue. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
146
+ * @summary Get the inbound SMS webhook URL
147
+ * @param {*} [options] Override http request option.
148
+ * @throws {RequiredError}
149
+ * @memberof SMSApiInterface
150
+ */
151
+ getSmsInboundWebhookRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>>;
152
+
153
+ /**
154
+ * Returns the URL to configure in your SMS provider so STOP/opt-out replies reach Zyphr. The token is generated on first request and is stable thereafter. **This is required for TCPA compliance.** Until your provider is pointed at this URL, opt-out replies stop at the provider and never reach us — from Zyphr\'s side the recipient never opted out, and sends continue. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
155
+ * Get the inbound SMS webhook URL
156
+ */
157
+ getSmsInboundWebhook(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void>;
158
+
144
159
  /**
145
160
  * List SMS messages for the account with optional filtering and pagination.
146
161
  * @summary List SMS messages
@@ -177,7 +192,7 @@ export interface SMSApiInterface {
177
192
  sendBatchSms(sendBatchSmsRequest: SendBatchSmsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SendBatchSmsResponse>;
178
193
 
179
194
  /**
180
- * Send a single SMS text message. Supports subscriber preference checks and scheduling.
195
+ * Send a single SMS text message. Supports subscriber preference checks and scheduling. **TCPA compliance — actions required from you:** - **Configure the inbound webhook.** Zyphr only processes STOP/opt-out replies if your SMS provider is pointed at our inbound endpoint. Until you do this, opt-outs never reach us and we will keep delivering. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements). - **Pass `recipient_timezone`** (IANA, e.g. `America/New_York`) so quiet hours (8am-9pm recipient-local) can be enforced. Without it we send and record that quiet hours could not be evaluated. We deliberately do not infer a timezone from the area code — people keep numbers when they move. - **Record consent** for marketing sends. Marketing requires prior express *written* consent; `express` consent covers transactional only. With no record on file we deliver and flag it, which is a gap in your evidence, not permission. - **Set `classification`.** Unclassified messages are treated as marketing, the stricter reading.
181
196
  * @summary Send an SMS message
182
197
  * @param {SendSmsRequest} sendSmsRequest
183
198
  * @param {*} [options] Override http request option.
@@ -187,7 +202,7 @@ export interface SMSApiInterface {
187
202
  sendSmsRaw(requestParameters: SMSApiSendSmsOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SendSmsResponse>>;
188
203
 
189
204
  /**
190
- * Send a single SMS text message. Supports subscriber preference checks and scheduling.
205
+ * Send a single SMS text message. Supports subscriber preference checks and scheduling. **TCPA compliance — actions required from you:** - **Configure the inbound webhook.** Zyphr only processes STOP/opt-out replies if your SMS provider is pointed at our inbound endpoint. Until you do this, opt-outs never reach us and we will keep delivering. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements). - **Pass `recipient_timezone`** (IANA, e.g. `America/New_York`) so quiet hours (8am-9pm recipient-local) can be enforced. Without it we send and record that quiet hours could not be evaluated. We deliberately do not infer a timezone from the area code — people keep numbers when they move. - **Record consent** for marketing sends. Marketing requires prior express *written* consent; `express` consent covers transactional only. With no record on file we deliver and flag it, which is a gap in your evidence, not permission. - **Set `classification`.** Unclassified messages are treated as marketing, the stricter reading.
191
206
  * Send an SMS message
192
207
  */
193
208
  sendSms(sendSmsRequest: SendSmsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SendSmsResponse>;
@@ -349,6 +364,37 @@ export class SMSApi extends runtime.BaseAPI implements SMSApiInterface {
349
364
  return await response.value();
350
365
  }
351
366
 
367
+ /**
368
+ * Returns the URL to configure in your SMS provider so STOP/opt-out replies reach Zyphr. The token is generated on first request and is stable thereafter. **This is required for TCPA compliance.** Until your provider is pointed at this URL, opt-out replies stop at the provider and never reach us — from Zyphr\'s side the recipient never opted out, and sends continue. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
369
+ * Get the inbound SMS webhook URL
370
+ */
371
+ async getSmsInboundWebhookRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
372
+ const queryParameters: any = {};
373
+
374
+ const headerParameters: runtime.HTTPHeaders = {};
375
+
376
+ if (this.configuration && this.configuration.apiKey) {
377
+ headerParameters["X-API-Key"] = await this.configuration.apiKey("X-API-Key"); // ApiKeyAuth authentication
378
+ }
379
+
380
+ const response = await this.request({
381
+ path: `/sms/inbound-webhook`,
382
+ method: 'GET',
383
+ headers: headerParameters,
384
+ query: queryParameters,
385
+ }, initOverrides);
386
+
387
+ return new runtime.VoidApiResponse(response);
388
+ }
389
+
390
+ /**
391
+ * Returns the URL to configure in your SMS provider so STOP/opt-out replies reach Zyphr. The token is generated on first request and is stable thereafter. **This is required for TCPA compliance.** Until your provider is pointed at this URL, opt-out replies stop at the provider and never reach us — from Zyphr\'s side the recipient never opted out, and sends continue. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements).
392
+ * Get the inbound SMS webhook URL
393
+ */
394
+ async getSmsInboundWebhook(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
395
+ await this.getSmsInboundWebhookRaw(initOverrides);
396
+ }
397
+
352
398
  /**
353
399
  * List SMS messages for the account with optional filtering and pagination.
354
400
  * List SMS messages
@@ -440,7 +486,7 @@ export class SMSApi extends runtime.BaseAPI implements SMSApiInterface {
440
486
  }
441
487
 
442
488
  /**
443
- * Send a single SMS text message. Supports subscriber preference checks and scheduling.
489
+ * Send a single SMS text message. Supports subscriber preference checks and scheduling. **TCPA compliance — actions required from you:** - **Configure the inbound webhook.** Zyphr only processes STOP/opt-out replies if your SMS provider is pointed at our inbound endpoint. Until you do this, opt-outs never reach us and we will keep delivering. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements). - **Pass `recipient_timezone`** (IANA, e.g. `America/New_York`) so quiet hours (8am-9pm recipient-local) can be enforced. Without it we send and record that quiet hours could not be evaluated. We deliberately do not infer a timezone from the area code — people keep numbers when they move. - **Record consent** for marketing sends. Marketing requires prior express *written* consent; `express` consent covers transactional only. With no record on file we deliver and flag it, which is a gap in your evidence, not permission. - **Set `classification`.** Unclassified messages are treated as marketing, the stricter reading.
444
490
  * Send an SMS message
445
491
  */
446
492
  async sendSmsRaw(requestParameters: SMSApiSendSmsOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SendSmsResponse>> {
@@ -473,7 +519,7 @@ export class SMSApi extends runtime.BaseAPI implements SMSApiInterface {
473
519
  }
474
520
 
475
521
  /**
476
- * Send a single SMS text message. Supports subscriber preference checks and scheduling.
522
+ * Send a single SMS text message. Supports subscriber preference checks and scheduling. **TCPA compliance — actions required from you:** - **Configure the inbound webhook.** Zyphr only processes STOP/opt-out replies if your SMS provider is pointed at our inbound endpoint. Until you do this, opt-outs never reach us and we will keep delivering. See [Compliance Requirements](https://zyphr.dev/guides/compliance-requirements). - **Pass `recipient_timezone`** (IANA, e.g. `America/New_York`) so quiet hours (8am-9pm recipient-local) can be enforced. Without it we send and record that quiet hours could not be evaluated. We deliberately do not infer a timezone from the area code — people keep numbers when they move. - **Record consent** for marketing sends. Marketing requires prior express *written* consent; `express` consent covers transactional only. With no record on file we deliver and flag it, which is a gap in your evidence, not permission. - **Set `classification`.** Unclassified messages are treated as marketing, the stricter reading.
477
523
  * Send an SMS message
478
524
  */
479
525
  async sendSms(sendSmsRequest: SendSmsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SendSmsResponse> {
@@ -35,7 +35,6 @@ import type {
35
35
  WebhookJobResponse,
36
36
  WebhookListResponse,
37
37
  WebhookMetricsResponse,
38
- WebhookRegionsResponse,
39
38
  WebhookReplayResponse,
40
39
  WebhookResponse,
41
40
  WebhookSecretRotateResponse,
@@ -85,8 +84,6 @@ import {
85
84
  WebhookListResponseToJSON,
86
85
  WebhookMetricsResponseFromJSON,
87
86
  WebhookMetricsResponseToJSON,
88
- WebhookRegionsResponseFromJSON,
89
- WebhookRegionsResponseToJSON,
90
87
  WebhookReplayResponseFromJSON,
91
88
  WebhookReplayResponseToJSON,
92
89
  WebhookResponseFromJSON,
@@ -373,21 +370,6 @@ export interface WebhooksApiInterface {
373
370
  */
374
371
  getWebhookMetrics(id: string, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<WebhookMetricsResponse>;
375
372
 
376
- /**
377
- * Get available webhook delivery regions. Requires multi_region plan feature (Enterprise only).
378
- * @summary Get available webhook regions
379
- * @param {*} [options] Override http request option.
380
- * @throws {RequiredError}
381
- * @memberof WebhooksApiInterface
382
- */
383
- getWebhookRegionsRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<WebhookRegionsResponse>>;
384
-
385
- /**
386
- * Get available webhook delivery regions. Requires multi_region plan feature (Enterprise only).
387
- * Get available webhook regions
388
- */
389
- getWebhookRegions(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<WebhookRegionsResponse>;
390
-
391
373
  /**
392
374
  * Get webhook delivery usage statistics for the current billing period.
393
375
  * @summary Get webhook usage metrics
@@ -1010,38 +992,6 @@ export class WebhooksApi extends runtime.BaseAPI implements WebhooksApiInterface
1010
992
  return await response.value();
1011
993
  }
1012
994
 
1013
- /**
1014
- * Get available webhook delivery regions. Requires multi_region plan feature (Enterprise only).
1015
- * Get available webhook regions
1016
- */
1017
- async getWebhookRegionsRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<WebhookRegionsResponse>> {
1018
- const queryParameters: any = {};
1019
-
1020
- const headerParameters: runtime.HTTPHeaders = {};
1021
-
1022
- if (this.configuration && this.configuration.apiKey) {
1023
- headerParameters["X-API-Key"] = await this.configuration.apiKey("X-API-Key"); // ApiKeyAuth authentication
1024
- }
1025
-
1026
- const response = await this.request({
1027
- path: `/v1/webhooks/regions`,
1028
- method: 'GET',
1029
- headers: headerParameters,
1030
- query: queryParameters,
1031
- }, initOverrides);
1032
-
1033
- return new runtime.JSONApiResponse(response, (jsonValue) => WebhookRegionsResponseFromJSON(jsonValue));
1034
- }
1035
-
1036
- /**
1037
- * Get available webhook delivery regions. Requires multi_region plan feature (Enterprise only).
1038
- * Get available webhook regions
1039
- */
1040
- async getWebhookRegions(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<WebhookRegionsResponse> {
1041
- const response = await this.getWebhookRegionsRaw(initOverrides);
1042
- return await response.value();
1043
- }
1044
-
1045
995
  /**
1046
996
  * Get webhook delivery usage statistics for the current billing period.
1047
997
  * Get webhook usage metrics
@@ -37,6 +37,29 @@ export interface InviteEndUser200ResponseData {
37
37
  * @memberof InviteEndUser200ResponseData
38
38
  */
39
39
  linkSent?: boolean;
40
+ /**
41
+ * Present only when `send_link` was requested and `link_sent` is false — explains why,
42
+ * so a caller can distinguish "suppressed by design" from "delivery failed".
43
+ *
44
+ * Known values:
45
+ *
46
+ * - `identity_has_other_auth_methods` — the identity has no password but does have a
47
+ * linked OAuth provider and/or a passkey, so it is not locked out and no
48
+ * set-password link is sent. The attempt is recorded as a suppressed message,
49
+ * visible via `GET /emails`.
50
+ * - `user_not_found_in_environment` — the identity could not be resolved in the
51
+ * calling environment (a race; it was created or resolved immediately prior).
52
+ * - `recipient_not_allowlisted` — the application's `test_recipients` allowlist
53
+ * blocked the recipient.
54
+ * - `send_failed` — dispatch was attempted and failed with no more specific code.
55
+ *
56
+ * Not a closed set: a mail-provider error code may be surfaced verbatim, so treat
57
+ * an unrecognised value as a generic failure rather than an error.
58
+ *
59
+ * @type {string}
60
+ * @memberof InviteEndUser200ResponseData
61
+ */
62
+ linkSuppressedReason?: string;
40
63
  }
41
64
 
42
65
  /**
@@ -59,6 +82,7 @@ export function InviteEndUser200ResponseDataFromJSONTyped(json: any, ignoreDiscr
59
82
  'userId': json['user_id'] == null ? undefined : json['user_id'],
60
83
  'created': json['created'] == null ? undefined : json['created'],
61
84
  'linkSent': json['link_sent'] == null ? undefined : json['link_sent'],
85
+ 'linkSuppressedReason': json['link_suppressed_reason'] == null ? undefined : json['link_suppressed_reason'],
62
86
  };
63
87
  }
64
88
 
@@ -76,6 +100,7 @@ export function InviteEndUser200ResponseDataToJSONTyped(value?: InviteEndUser200
76
100
  'user_id': value['userId'],
77
101
  'created': value['created'],
78
102
  'link_sent': value['linkSent'],
103
+ 'link_suppressed_reason': value['linkSuppressedReason'],
79
104
  };
80
105
  }
81
106
 
@@ -111,12 +111,6 @@ export interface WebhookDelivery {
111
111
  * @memberof WebhookDelivery
112
112
  */
113
113
  completedAt?: Date | null;
114
- /**
115
- *
116
- * @type {string}
117
- * @memberof WebhookDelivery
118
- */
119
- deliveryRegion?: string | null;
120
114
  /**
121
115
  *
122
116
  * @type {Date}
@@ -170,7 +164,6 @@ export function WebhookDeliveryFromJSONTyped(json: any, ignoreDiscriminator: boo
170
164
  'firstAttemptedAt': json['first_attempted_at'] == null ? undefined : (new Date(json['first_attempted_at'])),
171
165
  'lastAttemptedAt': json['last_attempted_at'] == null ? undefined : (new Date(json['last_attempted_at'])),
172
166
  'completedAt': json['completed_at'] == null ? undefined : (new Date(json['completed_at'])),
173
- 'deliveryRegion': json['delivery_region'] == null ? undefined : json['delivery_region'],
174
167
  'createdAt': json['created_at'] == null ? undefined : (new Date(json['created_at'])),
175
168
  };
176
169
  }
@@ -200,7 +193,6 @@ export function WebhookDeliveryToJSONTyped(value?: WebhookDelivery | null, ignor
200
193
  'first_attempted_at': value['firstAttemptedAt'] == null ? undefined : ((value['firstAttemptedAt'] as any).toISOString()),
201
194
  'last_attempted_at': value['lastAttemptedAt'] == null ? undefined : ((value['lastAttemptedAt'] as any).toISOString()),
202
195
  'completed_at': value['completedAt'] == null ? undefined : ((value['completedAt'] as any).toISOString()),
203
- 'delivery_region': value['deliveryRegion'],
204
196
  'created_at': value['createdAt'] == null ? undefined : ((value['createdAt']).toISOString()),
205
197
  };
206
198
  }
@@ -111,12 +111,6 @@ export interface WebhookDeliveryDetail {
111
111
  * @memberof WebhookDeliveryDetail
112
112
  */
113
113
  completedAt?: Date | null;
114
- /**
115
- *
116
- * @type {string}
117
- * @memberof WebhookDeliveryDetail
118
- */
119
- deliveryRegion?: string | null;
120
114
  /**
121
115
  *
122
116
  * @type {Date}
@@ -194,7 +188,6 @@ export function WebhookDeliveryDetailFromJSONTyped(json: any, ignoreDiscriminato
194
188
  'firstAttemptedAt': json['first_attempted_at'] == null ? undefined : (new Date(json['first_attempted_at'])),
195
189
  'lastAttemptedAt': json['last_attempted_at'] == null ? undefined : (new Date(json['last_attempted_at'])),
196
190
  'completedAt': json['completed_at'] == null ? undefined : (new Date(json['completed_at'])),
197
- 'deliveryRegion': json['delivery_region'] == null ? undefined : json['delivery_region'],
198
191
  'createdAt': json['created_at'] == null ? undefined : (new Date(json['created_at'])),
199
192
  'payload': json['payload'] == null ? undefined : json['payload'],
200
193
  'lastResponseBody': json['last_response_body'] == null ? undefined : json['last_response_body'],
@@ -228,7 +221,6 @@ export function WebhookDeliveryDetailToJSONTyped(value?: WebhookDeliveryDetail |
228
221
  'first_attempted_at': value['firstAttemptedAt'] == null ? undefined : ((value['firstAttemptedAt'] as any).toISOString()),
229
222
  'last_attempted_at': value['lastAttemptedAt'] == null ? undefined : ((value['lastAttemptedAt'] as any).toISOString()),
230
223
  'completed_at': value['completedAt'] == null ? undefined : ((value['completedAt'] as any).toISOString()),
231
- 'delivery_region': value['deliveryRegion'],
232
224
  'created_at': value['createdAt'] == null ? undefined : ((value['createdAt']).toISOString()),
233
225
  'payload': value['payload'],
234
226
  'last_response_body': value['lastResponseBody'],
@@ -482,9 +482,6 @@ export * from './WebhookIpsResponseData';
482
482
  export * from './WebhookJobResponse';
483
483
  export * from './WebhookListResponse';
484
484
  export * from './WebhookMetricsResponse';
485
- export * from './WebhookRegion';
486
- export * from './WebhookRegionsResponse';
487
- export * from './WebhookRegionsResponseData';
488
485
  export * from './WebhookReplayResponse';
489
486
  export * from './WebhookResponse';
490
487
  export * from './WebhookSecretRotateResponse';
@@ -1,81 +0,0 @@
1
- /* tslint:disable */
2
- /* eslint-disable */
3
- /**
4
- * Zyphr API
5
- * Zyphr is a multi-channel notification platform that enables developers to send emails, push notifications, SMS, and in-app messages through a unified API. ## Authentication All API requests require authentication using an API key. Include your API key in the `X-API-Key` header: ``` X-API-Key: zy_live_xxxxxxxxxxxx ``` API keys can be created in the Zyphr Dashboard. Use `zy_test_*` keys for testing and `zy_live_*` keys for production. ## Rate Limiting The API implements rate limiting to ensure fair usage. Rate limit information is included in response headers: - `X-RateLimit-Limit`: Maximum requests per window - `X-RateLimit-Remaining`: Remaining requests in current window - `X-RateLimit-Reset`: Unix timestamp when the window resets ## Account Sending Status Every authenticated response carries the account\'s current sending state so you can surface it in your own tooling without a separate call: - `X-Account-Sending-Status`: `active`, `throttled`, or `frozen` - `X-Account-Sending-Status-Reason`: human-readable reason (present only when the status is `throttled` or `frozen`) This header is informational and non-blocking on non-send endpoints (reads, billing, and deliverability stay available). Send-capable endpoints additionally enforce the state: a frozen account receives `403 account_frozen` and a throttled account receives `429 account_throttled`, both with the reason in the error message. ## Errors All errors follow a consistent format: ```json { \"error\": { \"code\": \"error_code\", \"message\": \"Human readable message\", \"details\": {} }, \"meta\": { \"request_id\": \"req_xxxx\" } } ```
6
- *
7
- * The version of the OpenAPI document: 1.0.0
8
- * Contact: support@zyphr.dev
9
- *
10
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
11
- * https://openapi-generator.tech
12
- * Do not edit the class manually.
13
- */
14
-
15
- import { mapValues } from '../runtime';
16
- /**
17
- *
18
- * @export
19
- * @interface WebhookRegion
20
- */
21
- export interface WebhookRegion {
22
- /**
23
- *
24
- * @type {string}
25
- * @memberof WebhookRegion
26
- */
27
- code?: string;
28
- /**
29
- *
30
- * @type {string}
31
- * @memberof WebhookRegion
32
- */
33
- name?: string;
34
- /**
35
- *
36
- * @type {Array<string>}
37
- * @memberof WebhookRegion
38
- */
39
- staticIps?: Array<string>;
40
- }
41
-
42
- /**
43
- * Check if a given object implements the WebhookRegion interface.
44
- */
45
- export function instanceOfWebhookRegion(value: object): value is WebhookRegion {
46
- return true;
47
- }
48
-
49
- export function WebhookRegionFromJSON(json: any): WebhookRegion {
50
- return WebhookRegionFromJSONTyped(json, false);
51
- }
52
-
53
- export function WebhookRegionFromJSONTyped(json: any, ignoreDiscriminator: boolean): WebhookRegion {
54
- if (json == null) {
55
- return json;
56
- }
57
- return {
58
-
59
- 'code': json['code'] == null ? undefined : json['code'],
60
- 'name': json['name'] == null ? undefined : json['name'],
61
- 'staticIps': json['static_ips'] == null ? undefined : json['static_ips'],
62
- };
63
- }
64
-
65
- export function WebhookRegionToJSON(json: any): WebhookRegion {
66
- return WebhookRegionToJSONTyped(json, false);
67
- }
68
-
69
- export function WebhookRegionToJSONTyped(value?: WebhookRegion | null, ignoreDiscriminator: boolean = false): any {
70
- if (value == null) {
71
- return value;
72
- }
73
-
74
- return {
75
-
76
- 'code': value['code'],
77
- 'name': value['name'],
78
- 'static_ips': value['staticIps'],
79
- };
80
- }
81
-