@easybits.cloud/mailmask 0.4.12 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -0
- package/dist/client.d.ts +18 -1
- package/dist/index.cjs +948 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +889 -5
- package/dist/partner-contract.d.ts +858 -0
- package/dist/partner.d.ts +398 -0
- package/package.json +1 -1
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
/** Prefijo de la API. Todas las rutas de partner cuelgan de aquí. */
|
|
2
|
+
export declare const PARTNER_API_PREFIX = "/api/partner/v1";
|
|
3
|
+
/** Headers que manda el servidor del partner en CADA petición. */
|
|
4
|
+
export declare const PARTNER_HEADERS: {
|
|
5
|
+
/** Id público de la credencial: `mmk_…`. */
|
|
6
|
+
readonly key: "X-MailMask-Key";
|
|
7
|
+
/** Segundos Unix (no milisegundos) del momento de firmar. */
|
|
8
|
+
readonly ts: "X-MailMask-Ts";
|
|
9
|
+
/** HMAC-SHA256 en hex minúsculas (64 caracteres), sin prefijo. */
|
|
10
|
+
readonly sig: "X-MailMask-Sig";
|
|
11
|
+
/** Opcional en `send`/`batch`: mismo efecto que `idempotencyKey` en el cuerpo. */
|
|
12
|
+
readonly idempotencyKey: "Idempotency-Key";
|
|
13
|
+
/**
|
|
14
|
+
* Opcional en `send`/`batch`: `1` = modo de prueba. Valida todo igual (firma, tenant, dominio,
|
|
15
|
+
* cuerpo, variables, supresión, cuota) y responde como de verdad con `test: true`, pero no envía
|
|
16
|
+
* a SES, no consume cuota, no cuenta como tenant activo y no emite eventos al webhook. En las
|
|
17
|
+
* demás rutas se ignora.
|
|
18
|
+
*/
|
|
19
|
+
readonly test: "X-MailMask-Test";
|
|
20
|
+
};
|
|
21
|
+
/** Ventana de la firma: el servidor rechaza un `ts` a más de 300 s de su reloj. */
|
|
22
|
+
export declare const PARTNER_SIGNATURE_TOLERANCE_S = 300;
|
|
23
|
+
/**
|
|
24
|
+
* Texto canónico que se firma con HMAC-SHA256 usando el secreto `mms_…` como llave:
|
|
25
|
+
*
|
|
26
|
+
* `${ts}.${keyId}.${METHOD}.${pathAndQuery}.${rawBody}`
|
|
27
|
+
*
|
|
28
|
+
* - `ts`: los mismos segundos que van en `X-MailMask-Ts`.
|
|
29
|
+
* - `METHOD`: en mayúsculas (`GET`, `PUT`, `POST`, `DELETE`).
|
|
30
|
+
* - `pathAndQuery`: la ruta tal como viaja, con su query (`/api/partner/v1/usage?month=2026-10`),
|
|
31
|
+
* sin esquema ni host.
|
|
32
|
+
* - `rawBody`: el cuerpo exacto en bytes UTF-8; cadena vacía si no hay cuerpo (GET, DELETE).
|
|
33
|
+
*
|
|
34
|
+
* El método y la ruta van dentro de la firma (el plan de gs firmaba sólo el cuerpo): sin ellos,
|
|
35
|
+
* un GET firmado —cuerpo vacío— se podría repetir contra CUALQUIER otra ruta de lectura durante
|
|
36
|
+
* los 300 s de la ventana.
|
|
37
|
+
*/
|
|
38
|
+
export declare function partnerSignatureBase(ts: string | number, keyId: string, method: string, pathAndQuery: string, rawBody: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Todo error responde JSON `{ error, code }` con el status de la tabla. `error` es texto en
|
|
41
|
+
* español para una persona; los programas deciden por `code` (y por el status).
|
|
42
|
+
*/
|
|
43
|
+
export declare const PARTNER_ERROR_STATUS: {
|
|
44
|
+
/** Firma ausente, mala, vencida, de una credencial revocada o inexistente: SIEMPRE el mismo 401. */
|
|
45
|
+
readonly unauthorized: 401;
|
|
46
|
+
/** Firma válida, pero el partner está desactivado o el tenant suspendido por MailMask. */
|
|
47
|
+
readonly forbidden: 403;
|
|
48
|
+
/** `resume` de una pausa que puso MailMask o SES por reputación: sólo MailMask la levanta. */
|
|
49
|
+
readonly reputation_hold: 403;
|
|
50
|
+
/** El tenant, dominio, lote o recurso no existe PARA ESTE partner (los de otros también dan 404). */
|
|
51
|
+
readonly not_found: 404;
|
|
52
|
+
/** El dominio ya es de otra cuenta o de otro tenant; máximo de credenciales; estado incompatible. */
|
|
53
|
+
readonly conflict: 409;
|
|
54
|
+
/** Validación: campos faltantes o inválidos, remitente fuera de los dominios del tenant,
|
|
55
|
+
* dominio sin verificar. */
|
|
56
|
+
readonly invalid_request: 422;
|
|
57
|
+
/** El destinatario está suprimido para este stream (con `reason`, `stream` y `recipient`). No es
|
|
58
|
+
* un error del cuerpo: no reintentes ni cambies el código, marca a la persona en tu sistema. */
|
|
59
|
+
readonly recipient_suppressed: 422;
|
|
60
|
+
/** El tenant está en pausa (por MailMask o por el partner). No se manda nada hasta `resume`. */
|
|
61
|
+
readonly tenant_paused: 423;
|
|
62
|
+
/**
|
|
63
|
+
* Saldo prepagado insuficiente (con `balanceCents` y `requiredCents`). Sólo cuando MailMask
|
|
64
|
+
* activó el cobro por adelantado del partner. No se reintenta: recarga con `POST /topup`.
|
|
65
|
+
*/
|
|
66
|
+
readonly insufficient_balance: 402;
|
|
67
|
+
/** Cuota mensual del tenant agotada. */
|
|
68
|
+
readonly quota_exceeded: 429;
|
|
69
|
+
/** Demasiadas peticiones; reintenta tras `Retry-After`. */
|
|
70
|
+
readonly rate_limited: 429;
|
|
71
|
+
/** Falla nuestra o de SES; reintentable con la misma `idempotencyKey`. */
|
|
72
|
+
readonly internal: 500;
|
|
73
|
+
};
|
|
74
|
+
export type PartnerErrorCode = keyof typeof PARTNER_ERROR_STATUS;
|
|
75
|
+
export interface PartnerErrorBody {
|
|
76
|
+
error: string;
|
|
77
|
+
code: PartnerErrorCode;
|
|
78
|
+
/** Sólo en `invalid_request`: el campo que falló, si aplica. */
|
|
79
|
+
field?: string;
|
|
80
|
+
/** Sólo en `quota_exceeded`: el tope del mes y cuándo se reinicia (ISO, día 1 a las 00:00 UTC). */
|
|
81
|
+
limit?: number;
|
|
82
|
+
resetsAt?: string;
|
|
83
|
+
/** Sólo en `insufficient_balance`: saldo actual y lo que pedía el envío (centavos MXN). */
|
|
84
|
+
balanceCents?: number;
|
|
85
|
+
requiredCents?: number;
|
|
86
|
+
/** Sólo en `recipient_suppressed`. */
|
|
87
|
+
reason?: SuppressionReason;
|
|
88
|
+
stream?: MessageStream;
|
|
89
|
+
recipient?: string;
|
|
90
|
+
}
|
|
91
|
+
/** `externalId`: el id del cliente en la app del partner. 1–128, empieza con letra o número. */
|
|
92
|
+
export declare const EXTERNAL_ID_RE: RegExp;
|
|
93
|
+
export declare function isValidExternalId(id: string): boolean;
|
|
94
|
+
/** Máximo de destinatarios por lote. */
|
|
95
|
+
export declare const MAX_BATCH_RECIPIENTS = 10000;
|
|
96
|
+
/** Máximo de etiquetas por envío; llave y valor `[A-Za-z0-9_-]{1,64}` (es lo que acepta SES). */
|
|
97
|
+
export declare const MAX_TAGS = 10;
|
|
98
|
+
export declare const TAG_RE: RegExp;
|
|
99
|
+
export type TenantStatus = "active" | "paused" | "suspended";
|
|
100
|
+
/** Quién puso la pausa. Sólo una pausa `partner` la levanta el partner. */
|
|
101
|
+
export type PausedBy = "partner" | "mailmask" | "ses";
|
|
102
|
+
export interface Tenant {
|
|
103
|
+
externalId: string;
|
|
104
|
+
name: string | null;
|
|
105
|
+
/** `paused`: lo pausó la pausa automática o el partner (423 al enviar). `suspended`: MailMask (403). */
|
|
106
|
+
status: TenantStatus;
|
|
107
|
+
pausedReason: string | null;
|
|
108
|
+
/** null si no está en pausa. */
|
|
109
|
+
pausedBy: PausedBy | null;
|
|
110
|
+
/** Dominios del tenant, en minúsculas. */
|
|
111
|
+
domains: string[];
|
|
112
|
+
lastSendAt: string | null;
|
|
113
|
+
createdAt: string;
|
|
114
|
+
updatedAt: string;
|
|
115
|
+
}
|
|
116
|
+
/** `PUT /tenants/:externalId` — crea o actualiza. 201 si lo creó, 200 si ya existía. */
|
|
117
|
+
export interface PutTenantRequest {
|
|
118
|
+
name?: string;
|
|
119
|
+
}
|
|
120
|
+
export type PutTenantResponse = Tenant;
|
|
121
|
+
/** `GET /tenants?status=&limit=&cursor=` — orden por `createdAt`, `limit` 1–200 (default 50). */
|
|
122
|
+
export interface ListTenantsQuery {
|
|
123
|
+
status?: TenantStatus;
|
|
124
|
+
limit?: number;
|
|
125
|
+
cursor?: string;
|
|
126
|
+
}
|
|
127
|
+
export interface ListTenantsResponse {
|
|
128
|
+
tenants: Tenant[];
|
|
129
|
+
/** null = no hay más. */
|
|
130
|
+
nextCursor: string | null;
|
|
131
|
+
}
|
|
132
|
+
/** `GET /tenants/:externalId` */
|
|
133
|
+
export type GetTenantResponse = Tenant;
|
|
134
|
+
/**
|
|
135
|
+
* `DELETE /tenants/:externalId` — borra el tenant y suelta sus dominios (los que nadie más usa se
|
|
136
|
+
* borran con sus recursos en SES). 409 si está en pausa por reputación (`mailmask` o `ses`). Los
|
|
137
|
+
* números de la pausa (ventana de 7 días) son por partner y dominio: recrear el tenant no los borra.
|
|
138
|
+
*/
|
|
139
|
+
export interface DeleteTenantResponse {
|
|
140
|
+
ok: true;
|
|
141
|
+
removedDomains: string[];
|
|
142
|
+
}
|
|
143
|
+
/** `POST /tenants/:externalId/pause` */
|
|
144
|
+
export interface PauseTenantRequest {
|
|
145
|
+
reason?: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `POST /tenants/:externalId/pause` y `/resume` devuelven el tenant ya cambiado. `resume` sólo
|
|
149
|
+
* levanta pausas `partner`; las de `mailmask` o `ses` dan 403 `reputation_hold`.
|
|
150
|
+
*/
|
|
151
|
+
export type PauseTenantResponse = Tenant;
|
|
152
|
+
export type ResumeTenantResponse = Tenant;
|
|
153
|
+
export interface PartnerDnsRecord {
|
|
154
|
+
/** `claim`, `mx`, `verification`, `dkim1..3`, `spf`, `dmarc`, `mailfrom-mx`, `mailfrom-spf`. */
|
|
155
|
+
id: string;
|
|
156
|
+
type: "MX" | "TXT" | "CNAME";
|
|
157
|
+
/** Relativo al dominio, como lo piden casi todos los paneles (`@`, `_amazonses`, `bounce`). */
|
|
158
|
+
name: string;
|
|
159
|
+
/** Nombre completo. */
|
|
160
|
+
fqdn: string;
|
|
161
|
+
value: string;
|
|
162
|
+
/** Sólo MX. */
|
|
163
|
+
priority?: number;
|
|
164
|
+
/** `requerido` para enviar; el MX raíz es `opcional` (sólo para recibir respuestas). */
|
|
165
|
+
level: "requerido" | "recomendado" | "opcional";
|
|
166
|
+
purpose: string;
|
|
167
|
+
/** Con estado: si el DNS público ya lo tiene. null = no se pudo consultar. */
|
|
168
|
+
ok?: boolean | null;
|
|
169
|
+
}
|
|
170
|
+
export type DomainState = "pending" | "verified" | "failed";
|
|
171
|
+
/** Un dominio apartado que no se verifica (identidad + `claim`) en estos días se suelta solo. */
|
|
172
|
+
export declare const DOMAIN_CLAIM_TTL_DAYS = 7;
|
|
173
|
+
/**
|
|
174
|
+
* Un dominio puede estar en tenants de varios partners (la panadería que usa Formmy y Deník): la
|
|
175
|
+
* identidad de SES es una sola y cada tenant prueba que el dueño del dominio lo autorizó con su
|
|
176
|
+
* propio TXT `_mailmask` (`mailmask-claim=<token>`, registro `claim`). Ningún partner ve qué otros
|
|
177
|
+
* comparten el dominio.
|
|
178
|
+
*/
|
|
179
|
+
export interface PartnerDomain {
|
|
180
|
+
domain: string;
|
|
181
|
+
/** `verified` = identidad de SES verificada Y `claimed`: ya se puede enviar desde este tenant. */
|
|
182
|
+
state: DomainState;
|
|
183
|
+
/** Identidad de SES verificada (TXT `_amazonses` o DKIM). */
|
|
184
|
+
verified: boolean;
|
|
185
|
+
dkimVerified: boolean;
|
|
186
|
+
/** MAIL FROM `bounce.<dominio>` verificado: con él el SPF se alinea. */
|
|
187
|
+
mailFromDomain: string;
|
|
188
|
+
mailFromVerified: boolean;
|
|
189
|
+
/** El TXT `claim` de ESTE tenant ya se vio en el DNS público. */
|
|
190
|
+
claimed: boolean;
|
|
191
|
+
/** Si no se reclama antes, el dominio se suelta de este tenant. null = ya reclamado. */
|
|
192
|
+
claimExpiresAt: string | null;
|
|
193
|
+
/** true = la zona es de MailMask y los registros ya se escribieron solos. */
|
|
194
|
+
autoDns: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* true = subdominio de la zona de respaldo del partner (`<etiqueta>.marketing.denik.me`): los
|
|
197
|
+
* registros los escribe MailMask y se verifica solo, sin que el cliente toque nada.
|
|
198
|
+
*/
|
|
199
|
+
fallback: boolean;
|
|
200
|
+
records: PartnerDnsRecord[];
|
|
201
|
+
createdAt: string;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* `POST /tenants/:externalId/domains` — 201 al agregarlo al tenant, 200 si ya estaba. 409 si el
|
|
205
|
+
* dominio es de una cuenta directa de MailMask (su dueño tendría que soltarlo).
|
|
206
|
+
*
|
|
207
|
+
* Dominio de respaldo: `<etiqueta>.<zona de respaldo del partner>` (p. ej.
|
|
208
|
+
* `estudio-aurora.marketing.denik.me`). La etiqueta es UNA sola (`FALLBACK_LABEL_RE`, sin las de
|
|
209
|
+
* `RESERVED_FALLBACK_LABELS`), es de un solo tenant (otro tenant u otro partner: 409) y cada tenant
|
|
210
|
+
* tiene a lo más un dominio de respaldo (409). 409 también si la zona todavía no está delegada.
|
|
211
|
+
*/
|
|
212
|
+
export interface AddDomainRequest {
|
|
213
|
+
domain: string;
|
|
214
|
+
}
|
|
215
|
+
export type AddDomainResponse = PartnerDomain;
|
|
216
|
+
/** `GET /tenants/:externalId/domains/:domain` — consulta SES y el DNS público en vivo. */
|
|
217
|
+
export type GetDomainResponse = PartnerDomain;
|
|
218
|
+
/** Etiqueta de un dominio de respaldo: un solo nombre DNS en minúsculas, 1–63 caracteres. */
|
|
219
|
+
export declare const FALLBACK_LABEL_RE: RegExp;
|
|
220
|
+
/** Etiquetas que no se dan a un tenant: confundirían a quien lee el remitente o chocan con servicios. */
|
|
221
|
+
export declare const RESERVED_FALLBACK_LABELS: readonly string[];
|
|
222
|
+
/** ¿`label` sirve como etiqueta de un dominio de respaldo? */
|
|
223
|
+
export declare function isValidFallbackLabel(label: string): boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Tipo de correo. `transactional` (confirmaciones, recordatorios, contraseñas) nunca lleva baja de
|
|
226
|
+
* un clic y no lo calla una baja del boletín. `broadcast` (boletines, promociones) lleva
|
|
227
|
+
* `List-Unsubscribe` y su baja sólo suprime `broadcast` de ESE tenant. Rebotes duros y quejas
|
|
228
|
+
* suprimen los dos.
|
|
229
|
+
*/
|
|
230
|
+
export type MessageStream = "transactional" | "broadcast";
|
|
231
|
+
export declare const MESSAGE_STREAMS: readonly MessageStream[];
|
|
232
|
+
/**
|
|
233
|
+
* Por qué un destinatario está suprimido. `bounce`, `complaint` y `manual` valen para el dominio
|
|
234
|
+
* entero y todos los streams; `unsubscribe` sólo para `broadcast` del tenant que lo envió.
|
|
235
|
+
*/
|
|
236
|
+
export type SuppressionReason = "bounce" | "complaint" | "unsubscribe" | "manual";
|
|
237
|
+
/**
|
|
238
|
+
* Variables que se sustituyen en `subject`, `html` y `text` con marcadores `{{nombre}}` (se
|
|
239
|
+
* permiten espacios: `{{ nombre }}`). Dentro de `html` el valor se escapa (`<`, `>`, `&`, comillas);
|
|
240
|
+
* en `subject` y `text` va tal cual. Un marcador sin variable queda en cadena vacía, así que un
|
|
241
|
+
* `{{` literal en tu plantilla también se sustituye.
|
|
242
|
+
*/
|
|
243
|
+
export type TemplateVars = Record<string, string>;
|
|
244
|
+
/** Nombre de una variable: letra o `_`, luego letras, números o `_`. */
|
|
245
|
+
export declare const TEMPLATE_VAR_NAME_RE: RegExp;
|
|
246
|
+
/** Por destinatario: hasta 50 variables, nombres de hasta 64 caracteres y valores de hasta 2000. */
|
|
247
|
+
export declare const MAX_TEMPLATE_VARS = 50;
|
|
248
|
+
export declare const MAX_TEMPLATE_VAR_NAME = 64;
|
|
249
|
+
export declare const MAX_TEMPLATE_VAR_VALUE = 2000;
|
|
250
|
+
/**
|
|
251
|
+
* Variable reservada: la pone MailMask en cada correo `broadcast` con la liga de baja de un clic
|
|
252
|
+
* de ESE destinatario (la misma de `List-Unsubscribe`). Si el `html` de un `broadcast` no la usa,
|
|
253
|
+
* MailMask agrega al final un pie discreto con la liga (y al `text`, una línea). En `transactional`
|
|
254
|
+
* no existe (queda vacía). No se puede mandar en `vars`.
|
|
255
|
+
*/
|
|
256
|
+
export declare const UNSUBSCRIBE_URL_VAR = "unsubscribe_url";
|
|
257
|
+
/**
|
|
258
|
+
* Sustituye los `{{nombre}}` de una plantilla: la misma función que usa el servidor, para que el
|
|
259
|
+
* partner pueda previsualizar exactamente lo que va a salir. Con `html: true` escapa los valores.
|
|
260
|
+
*/
|
|
261
|
+
export declare function renderTemplate(template: string, vars: TemplateVars, opts?: {
|
|
262
|
+
html?: boolean;
|
|
263
|
+
}): string;
|
|
264
|
+
/** ¿La plantilla usa el marcador `{{name}}`? */
|
|
265
|
+
export declare function templateUsesVar(template: string, name: string): boolean;
|
|
266
|
+
/**
|
|
267
|
+
* Lo que sale para UN destinatario: sus variables en `subject`, `html` (escapadas) y `text`, y en
|
|
268
|
+
* un broadcast (`unsubscribeUrl` no null) la liga de baja en `{{unsubscribe_url}}` o, si el
|
|
269
|
+
* partner no la puso, en un pie. Un `text` ausente se queda ausente: lo deriva el pipeline del
|
|
270
|
+
* `html` ya personalizado, pie incluido.
|
|
271
|
+
*/
|
|
272
|
+
export declare function personalizePartnerMessage(input: {
|
|
273
|
+
subject: string;
|
|
274
|
+
html?: string;
|
|
275
|
+
text?: string;
|
|
276
|
+
vars?: TemplateVars;
|
|
277
|
+
unsubscribeUrl: string | null;
|
|
278
|
+
}): {
|
|
279
|
+
subject: string;
|
|
280
|
+
html?: string;
|
|
281
|
+
text?: string;
|
|
282
|
+
};
|
|
283
|
+
export interface SendRequest {
|
|
284
|
+
/** `ventas@cliente.com` o `Nombre <ventas@cliente.com>`; el dominio debe ser del tenant y estar verificado. */
|
|
285
|
+
from: string;
|
|
286
|
+
to: string;
|
|
287
|
+
subject: string;
|
|
288
|
+
/** Al menos uno de `html` o `text`. Si sólo hay `html`, el texto plano se deriva. */
|
|
289
|
+
html?: string;
|
|
290
|
+
text?: string;
|
|
291
|
+
replyTo?: string;
|
|
292
|
+
/** Regresan en los eventos del webhook (`data.tags`). Ver `MAX_TAGS` y `TAG_RE`. */
|
|
293
|
+
tags?: Record<string, string>;
|
|
294
|
+
/** Default `transactional`. */
|
|
295
|
+
stream?: MessageStream;
|
|
296
|
+
/** Variables para los `{{nombre}}` de `subject`, `html` y `text` (ver `TemplateVars`). */
|
|
297
|
+
vars?: TemplateVars;
|
|
298
|
+
/** Reintentar con la misma llave en 24 h devuelve la respuesta guardada sin volver a enviar. */
|
|
299
|
+
idempotencyKey?: string;
|
|
300
|
+
}
|
|
301
|
+
/** `POST /tenants/:externalId/send` — 200. Con `Idempotent-Replayed: true` si fue repetición. */
|
|
302
|
+
export interface SendResponse {
|
|
303
|
+
/** Message-ID del correo (con ángulos). */
|
|
304
|
+
messageId: string;
|
|
305
|
+
/** Id de SES; es el que aparece en los eventos. En modo de prueba empieza con `test-`. */
|
|
306
|
+
sesMessageId: string;
|
|
307
|
+
status: "sent";
|
|
308
|
+
/** Sólo en modo de prueba (`X-MailMask-Test: 1`): no salió nada. */
|
|
309
|
+
test?: true;
|
|
310
|
+
}
|
|
311
|
+
/** Un destinatario del lote con sus variables (`{{nombre}}` en `subject`, `html` y `text`). */
|
|
312
|
+
export interface BatchRecipient {
|
|
313
|
+
to: string;
|
|
314
|
+
vars?: TemplateVars;
|
|
315
|
+
}
|
|
316
|
+
export interface BatchRequest {
|
|
317
|
+
from: string;
|
|
318
|
+
/** Plantillas: `{{nombre}}` se sustituye por destinatario (ver `TemplateVars`). */
|
|
319
|
+
subject: string;
|
|
320
|
+
html: string;
|
|
321
|
+
text?: string;
|
|
322
|
+
replyTo?: string;
|
|
323
|
+
tags?: Record<string, string>;
|
|
324
|
+
/**
|
|
325
|
+
* 1 a `MAX_BATCH_RECIPIENTS`: el correo pelado o `{ to, vars }`; se pueden mezclar. Los
|
|
326
|
+
* suprimidos se saltan (cuentan en `skippedSuppressed`).
|
|
327
|
+
*/
|
|
328
|
+
recipients: Array<string | BatchRecipient>;
|
|
329
|
+
/** Default `broadcast` (lleva baja de un clic por destinatario). */
|
|
330
|
+
stream?: MessageStream;
|
|
331
|
+
idempotencyKey?: string;
|
|
332
|
+
}
|
|
333
|
+
export type BatchState = "queued" | "processing" | "completed" | "failed";
|
|
334
|
+
export interface BatchStatus {
|
|
335
|
+
batchId: string;
|
|
336
|
+
status: BatchState;
|
|
337
|
+
stream: MessageStream;
|
|
338
|
+
total: number;
|
|
339
|
+
sent: number;
|
|
340
|
+
failed: number;
|
|
341
|
+
skippedSuppressed: number;
|
|
342
|
+
/**
|
|
343
|
+
* Lo que pasó después de enviar, contado con los mismos eventos que llegan al webhook (entregas,
|
|
344
|
+
* rebotes permanentes y quejas de SES; bajas de un clic desde un correo de este lote). Crecen
|
|
345
|
+
* durante horas después de `completed`; el lote se consulta hasta 7 días después de crearlo.
|
|
346
|
+
*/
|
|
347
|
+
delivered: number;
|
|
348
|
+
bounced: number;
|
|
349
|
+
complained: number;
|
|
350
|
+
unsubscribed: number;
|
|
351
|
+
lastError: string | null;
|
|
352
|
+
createdAt: string;
|
|
353
|
+
completedAt: string | null;
|
|
354
|
+
/**
|
|
355
|
+
* Sólo en modo de prueba: el lote se valida y responde `completed` al instante (los suprimidos en
|
|
356
|
+
* `skippedSuppressed`, el resto en `sent`), con `batchId` `test_…` que no se guarda (su GET da 404).
|
|
357
|
+
*/
|
|
358
|
+
test?: true;
|
|
359
|
+
}
|
|
360
|
+
/** `POST /tenants/:externalId/batch` — 202. */
|
|
361
|
+
export type BatchResponse = BatchStatus;
|
|
362
|
+
/** `GET /tenants/:externalId/batch/:batchId` */
|
|
363
|
+
export type GetBatchResponse = BatchStatus;
|
|
364
|
+
export interface TenantUsage {
|
|
365
|
+
externalId: string;
|
|
366
|
+
sent: number;
|
|
367
|
+
delivered: number;
|
|
368
|
+
bounced: number;
|
|
369
|
+
complained: number;
|
|
370
|
+
unsubscribed: number;
|
|
371
|
+
}
|
|
372
|
+
/** `GET /usage?month=YYYY-MM` (default: el mes en curso, UTC). Activo = al menos un envío en el mes. */
|
|
373
|
+
export interface UsageResponse {
|
|
374
|
+
month: string;
|
|
375
|
+
activeTenants: number;
|
|
376
|
+
sent: number;
|
|
377
|
+
tenants: TenantUsage[];
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* El partner paga por adelantado: cada correo y la cuota mensual de cada tenant activo se
|
|
381
|
+
* descuentan de un saldo que se recarga con MercadoPago (`POST /topup`). Todo en centavos de MXN.
|
|
382
|
+
*/
|
|
383
|
+
/** Precio por correo enviado (por destinatario). */
|
|
384
|
+
export declare const PARTNER_PRICE_PER_EMAIL_CENTS = 6;
|
|
385
|
+
/**
|
|
386
|
+
* Cuota mensual por tenant activo. Activo = envía ese mes: se descuenta con su primer envío del
|
|
387
|
+
* mes (UTC), una sola vez, así que un tenant dormido no cuesta nada.
|
|
388
|
+
*/
|
|
389
|
+
export declare const PARTNER_TENANT_MONTH_CENTS = 4900;
|
|
390
|
+
/** Recarga mínima y máxima por pago. */
|
|
391
|
+
export declare const PARTNER_TOPUP_MIN_CENTS = 50000;
|
|
392
|
+
export declare const PARTNER_TOPUP_MAX_CENTS = 10000000;
|
|
393
|
+
/** `balance.low` sale cuando el saldo baja del 20 % de la última recarga (una vez por recarga). */
|
|
394
|
+
export declare const BALANCE_LOW_FRACTION = 0.2;
|
|
395
|
+
export type BalanceMovementKind = "topup" | "send" | "tenant_fee" | "refund" | "adjust";
|
|
396
|
+
export declare const BALANCE_MOVEMENT_KINDS: readonly BalanceMovementKind[];
|
|
397
|
+
export interface BalanceMovement {
|
|
398
|
+
id: number;
|
|
399
|
+
kind: BalanceMovementKind;
|
|
400
|
+
/** Positivo = abono (recarga, devolución, ajuste a favor); negativo = cargo. */
|
|
401
|
+
amountCents: number;
|
|
402
|
+
balanceAfterCents: number;
|
|
403
|
+
/** Tenant del cargo (`send`, `tenant_fee`, `refund`); null en recargas y ajustes. */
|
|
404
|
+
externalId: string | null;
|
|
405
|
+
/** Referencia: `batch:<id>`, mes de la cuota (`YYYY-MM`), pago de MercadoPago… */
|
|
406
|
+
ref: string | null;
|
|
407
|
+
note: string | null;
|
|
408
|
+
createdAt: string;
|
|
409
|
+
}
|
|
410
|
+
/** `GET /balance` */
|
|
411
|
+
export interface BalanceResponse {
|
|
412
|
+
currency: "MXN";
|
|
413
|
+
balanceCents: number;
|
|
414
|
+
/**
|
|
415
|
+
* true = sin saldo suficiente los envíos se rechazan (402 `insufficient_balance`) y los lotes
|
|
416
|
+
* esperan. false = el saldo se lleva igual (puede quedar negativo) pero no frena nada.
|
|
417
|
+
*/
|
|
418
|
+
enforced: boolean;
|
|
419
|
+
prices: {
|
|
420
|
+
perEmailCents: number;
|
|
421
|
+
tenantMonthCents: number;
|
|
422
|
+
topupMinCents: number;
|
|
423
|
+
};
|
|
424
|
+
lastTopup: {
|
|
425
|
+
amountCents: number;
|
|
426
|
+
at: string;
|
|
427
|
+
} | null;
|
|
428
|
+
/** Debajo de esto sale `balance.low`; null sin recargas. */
|
|
429
|
+
lowBalanceThresholdCents: number | null;
|
|
430
|
+
/** Mes en curso (UTC). */
|
|
431
|
+
month: string;
|
|
432
|
+
/** Tenants a los que ya se les cobró la cuota del mes. */
|
|
433
|
+
activeTenants: number;
|
|
434
|
+
/** Correos cobrados en el mes (sin los devueltos). */
|
|
435
|
+
monthEmails: number;
|
|
436
|
+
/** Lo gastado en el mes: correos + cuotas − devoluciones. */
|
|
437
|
+
monthSpentCents: number;
|
|
438
|
+
}
|
|
439
|
+
/** `GET /balance/movements?cursor=&limit=&kind=topup,adjust` — del más nuevo al más viejo. */
|
|
440
|
+
export interface BalanceMovementsQuery {
|
|
441
|
+
cursor?: string;
|
|
442
|
+
/** 1–200, default 50. */
|
|
443
|
+
limit?: number;
|
|
444
|
+
kind?: BalanceMovementKind[];
|
|
445
|
+
}
|
|
446
|
+
export interface BalanceMovementsResponse {
|
|
447
|
+
movements: BalanceMovement[];
|
|
448
|
+
nextCursor: string | null;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* `POST /topup` — liga de pago único de MercadoPago. El saldo sube cuando MercadoPago confirma
|
|
452
|
+
* el pago (webhook), no al crear la liga. `returnUrl` (https) es a dónde regresa el navegador.
|
|
453
|
+
*/
|
|
454
|
+
export interface TopupRequest {
|
|
455
|
+
/** Pesos MXN enteros, 500 a 100,000. */
|
|
456
|
+
amountMxn: number;
|
|
457
|
+
returnUrl?: string;
|
|
458
|
+
}
|
|
459
|
+
export interface TopupResponse {
|
|
460
|
+
topupId: string;
|
|
461
|
+
amountCents: number;
|
|
462
|
+
/** Abre esto en el navegador de quien paga. */
|
|
463
|
+
initPoint: string;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* `GET /api/partners/:partnerId/balance` (dueño o MailMask): el saldo, sus últimos 20 movimientos,
|
|
467
|
+
* el partner (con `tenants` y `ownerEmail`) y si quien mira es de MailMask (ve abonar y el cobro).
|
|
468
|
+
*/
|
|
469
|
+
export type PartnerBalanceDetail = BalanceResponse & {
|
|
470
|
+
movements: BalanceMovement[];
|
|
471
|
+
partner: PartnerSummary;
|
|
472
|
+
staff: boolean;
|
|
473
|
+
};
|
|
474
|
+
/** `POST /api/partners/:partnerId/balance/adjust` — sólo MailMask (admin): abono o cargo manual. */
|
|
475
|
+
export interface AdjustBalanceRequest {
|
|
476
|
+
/** Centavos MXN, distinto de 0 (negativo = cargo). */
|
|
477
|
+
amountCents: number;
|
|
478
|
+
note: string;
|
|
479
|
+
/** Repetir con la misma llave no vuelve a abonar. */
|
|
480
|
+
idempotencyKey?: string;
|
|
481
|
+
}
|
|
482
|
+
/** `PUT /api/partners/:partnerId/prepaid` — sólo MailMask (admin): prende o apaga el cobro por adelantado. */
|
|
483
|
+
export interface SetPrepaidEnforcedRequest {
|
|
484
|
+
enforced: boolean;
|
|
485
|
+
}
|
|
486
|
+
/** `GET /webhook` */
|
|
487
|
+
export interface WebhookConfig {
|
|
488
|
+
url: string | null;
|
|
489
|
+
events: PartnerWebhookEvent[];
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* `PUT /webhook` — https y host público. Cambiar la URL NO cambia el secreto: el `whsec_…` se crea
|
|
493
|
+
* la primera vez (y sólo entonces viene en la respuesta) y se rota aparte.
|
|
494
|
+
*/
|
|
495
|
+
export interface SetWebhookRequest {
|
|
496
|
+
url: string;
|
|
497
|
+
}
|
|
498
|
+
export interface SetWebhookResponse {
|
|
499
|
+
url: string;
|
|
500
|
+
/** `whsec_…` sólo la primera vez que se configura; null si ya existía. */
|
|
501
|
+
secret: string | null;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* `POST /webhook/rotate-secret` — secreto nuevo, que se ve UNA vez. Durante la gracia (0–10080 min,
|
|
505
|
+
* default 1440 = 24 h) cada entrega lleva dos firmas, la nueva y la vieja, separadas por coma.
|
|
506
|
+
*/
|
|
507
|
+
export interface RotateWebhookSecretRequest {
|
|
508
|
+
graceMinutes?: number;
|
|
509
|
+
}
|
|
510
|
+
export interface RotateWebhookSecretResponse {
|
|
511
|
+
secret: string;
|
|
512
|
+
/** Hasta cuándo se sigue firmando también con el secreto anterior. null = sin gracia. */
|
|
513
|
+
previousValidUntil: string | null;
|
|
514
|
+
}
|
|
515
|
+
/** `POST /webhook/test` — encola un `ping`. 202. */
|
|
516
|
+
export interface TestWebhookResponse {
|
|
517
|
+
deliveryId: string;
|
|
518
|
+
}
|
|
519
|
+
export declare const PARTNER_WEBHOOK_EVENTS: readonly ["email.sent", "email.delivered", "email.bounced", "email.complained", "email.received", "contact.unsubscribed", "tenant.paused", "tenant.resumed", "domain.verified", "balance.low"];
|
|
520
|
+
export type PartnerWebhookEvent = typeof PARTNER_WEBHOOK_EVENTS[number];
|
|
521
|
+
/** Headers de cada entrega al webhook del partner (los mismos que los webhooks de dominio). */
|
|
522
|
+
export declare const WEBHOOK_HEADERS: {
|
|
523
|
+
readonly event: "X-MailMask-Event";
|
|
524
|
+
readonly delivery: "X-MailMask-Delivery";
|
|
525
|
+
/** MILISEGUNDOS Unix, en texto (distinto de `X-MailMask-Ts` de las peticiones). */
|
|
526
|
+
readonly timestamp: "X-MailMask-Timestamp";
|
|
527
|
+
/**
|
|
528
|
+
* `sha256=` + hex de HMAC-SHA256(secreto `whsec_…`, `webhookSignatureBase(...)`). Durante la gracia
|
|
529
|
+
* de una rotación vienen dos separadas por coma (`sha256=<nueva>,sha256=<vieja>`): el receptor
|
|
530
|
+
* acepta la entrega si CUALQUIERA coincide con su secreto.
|
|
531
|
+
*/
|
|
532
|
+
readonly signature: "X-MailMask-Signature";
|
|
533
|
+
};
|
|
534
|
+
/** Tolerancia del receptor al verificar: 5 min, borde incluido. */
|
|
535
|
+
export declare const WEBHOOK_TOLERANCE_MS: number;
|
|
536
|
+
/** Texto que firma MailMask al entregar: `${timestamp}.${rawBody}` (contrato de docs/agents/firma-webhooks.md). */
|
|
537
|
+
export declare function webhookSignatureBase(timestamp: string, rawBody: string): string;
|
|
538
|
+
export interface EmailEventBase {
|
|
539
|
+
/** Dominio remitente (o receptor, en `email.received`). */
|
|
540
|
+
domain: string;
|
|
541
|
+
messageId: string | null;
|
|
542
|
+
sesMessageId: string | null;
|
|
543
|
+
subject: string | null;
|
|
544
|
+
tags: Record<string, string>;
|
|
545
|
+
}
|
|
546
|
+
export interface PartnerEventData {
|
|
547
|
+
"email.sent": EmailEventBase & {
|
|
548
|
+
from: string;
|
|
549
|
+
to: string;
|
|
550
|
+
batchId: string | null;
|
|
551
|
+
stream: MessageStream;
|
|
552
|
+
};
|
|
553
|
+
"email.delivered": EmailEventBase & {
|
|
554
|
+
recipient: string;
|
|
555
|
+
smtpResponse: string | null;
|
|
556
|
+
};
|
|
557
|
+
"email.bounced": EmailEventBase & {
|
|
558
|
+
recipient: string;
|
|
559
|
+
bounceType: "Permanent";
|
|
560
|
+
diagnostic: string | null;
|
|
561
|
+
};
|
|
562
|
+
"email.complained": EmailEventBase & {
|
|
563
|
+
recipient: string;
|
|
564
|
+
feedbackType: string | null;
|
|
565
|
+
};
|
|
566
|
+
"email.received": {
|
|
567
|
+
domain: string;
|
|
568
|
+
from: string;
|
|
569
|
+
to: string;
|
|
570
|
+
subject: string;
|
|
571
|
+
messageId: string | null;
|
|
572
|
+
inReplyTo: string | null;
|
|
573
|
+
};
|
|
574
|
+
/** La baja de un clic de un `broadcast`: suprime sólo `broadcast` de este tenant. */
|
|
575
|
+
"contact.unsubscribed": {
|
|
576
|
+
domain: string;
|
|
577
|
+
email: string;
|
|
578
|
+
source: "one-click" | "page";
|
|
579
|
+
stream: "broadcast";
|
|
580
|
+
};
|
|
581
|
+
"tenant.paused": {
|
|
582
|
+
reason: string;
|
|
583
|
+
by: PausedBy;
|
|
584
|
+
window?: {
|
|
585
|
+
sent: number;
|
|
586
|
+
bounceRate: number;
|
|
587
|
+
complaintRate: number;
|
|
588
|
+
};
|
|
589
|
+
};
|
|
590
|
+
"tenant.resumed": {
|
|
591
|
+
by: "partner" | "mailmask";
|
|
592
|
+
};
|
|
593
|
+
"domain.verified": {
|
|
594
|
+
domain: string;
|
|
595
|
+
mailFromVerified: boolean;
|
|
596
|
+
};
|
|
597
|
+
/** El saldo prepagado bajó del 20 % de la última recarga. `externalId` va null. */
|
|
598
|
+
"balance.low": {
|
|
599
|
+
balanceCents: number;
|
|
600
|
+
thresholdCents: number;
|
|
601
|
+
lastTopupCents: number;
|
|
602
|
+
};
|
|
603
|
+
ping: Record<string, never>;
|
|
604
|
+
}
|
|
605
|
+
/** Sobre de cada evento. `externalId` es null en `ping` y `balance.low`. */
|
|
606
|
+
export interface PartnerWebhookPayload<E extends keyof PartnerEventData = keyof PartnerEventData> {
|
|
607
|
+
/** Id de la entrega (igual que `X-MailMask-Delivery`); úsalo para descartar duplicados. */
|
|
608
|
+
id: string;
|
|
609
|
+
event: E;
|
|
610
|
+
externalId: string | null;
|
|
611
|
+
/** ISO del momento en que ocurrió. */
|
|
612
|
+
timestamp: string;
|
|
613
|
+
data: PartnerEventData[E];
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Política de reputación de SES para los tenants del partner. Strict pausa el envío antes ante
|
|
617
|
+
* rebotes y quejas; Standard es la de AWS por defecto.
|
|
618
|
+
*/
|
|
619
|
+
export type ReputationPolicy = "standard" | "strict";
|
|
620
|
+
/** Primer mes del partner: sus tenants van en Strict salvo que MailMask fije otra política. */
|
|
621
|
+
export declare const STRICT_FIRST_DAYS = 30;
|
|
622
|
+
/** `GET /api/partners` — los partners de la cuenta. MailMask (admin) con `?all=1`: todos, con `tenants`. */
|
|
623
|
+
export interface PartnerSummary {
|
|
624
|
+
id: string;
|
|
625
|
+
name: string;
|
|
626
|
+
plan: {
|
|
627
|
+
includedTenants: number;
|
|
628
|
+
sendsPerTenantMonth: number;
|
|
629
|
+
};
|
|
630
|
+
webhookUrl: string | null;
|
|
631
|
+
disabledAt: string | null;
|
|
632
|
+
createdAt: string;
|
|
633
|
+
/** La que fijó MailMask; null = automática (Strict el primer mes, Standard después). */
|
|
634
|
+
reputationPolicy: ReputationPolicy | null;
|
|
635
|
+
/** La que se aplica hoy a sus tenants. */
|
|
636
|
+
effectiveReputationPolicy: ReputationPolicy;
|
|
637
|
+
/** Zona de respaldo (`marketing.denik.me`); null = sin zona. Ver `FallbackZone`. */
|
|
638
|
+
fallbackZone: string | null;
|
|
639
|
+
/** Saldo prepagado (centavos MXN) y si ya frena envíos. Ver `BalanceResponse`. */
|
|
640
|
+
balanceCents: number;
|
|
641
|
+
prepaidEnforced: boolean;
|
|
642
|
+
/** Sólo en `GET /api/partners?all=1` (MailMask, admin): tenants totales y activos del mes. */
|
|
643
|
+
tenants?: {
|
|
644
|
+
total: number;
|
|
645
|
+
activeThisMonth: number;
|
|
646
|
+
};
|
|
647
|
+
/** Sólo en `?all=1` (admin): cuenta dueña. */
|
|
648
|
+
ownerEmail?: string;
|
|
649
|
+
}
|
|
650
|
+
/** `POST /api/partners` — alta (sólo MailMask, admin). 201 con `PartnerSummary`. */
|
|
651
|
+
export interface CreatePartnerRequest {
|
|
652
|
+
name: string;
|
|
653
|
+
/** Cuenta de MailMask que será dueña del partner; debe existir. */
|
|
654
|
+
ownerEmail: string;
|
|
655
|
+
plan?: Partial<PartnerSummary["plan"]>;
|
|
656
|
+
reputationPolicy?: ReputationPolicy | null;
|
|
657
|
+
}
|
|
658
|
+
/** `PUT /api/partners/:partnerId/reputation-policy` — sólo MailMask (admin); null = automática. */
|
|
659
|
+
export interface SetReputationPolicyRequest {
|
|
660
|
+
policy: ReputationPolicy | null;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* `PUT /api/partners/:partnerId/fallback-zone` — sólo MailMask (admin). Crea (o adopta) la hosted
|
|
664
|
+
* zone de Route 53 para `zone` y devuelve los 4 NS que el partner tiene que delegar en SU DNS
|
|
665
|
+
* (registros NS de `zone` en la zona padre). Repetirlo con la misma zona es idempotente; cambiarla
|
|
666
|
+
* por otra responde 409.
|
|
667
|
+
*/
|
|
668
|
+
export interface SetFallbackZoneRequest {
|
|
669
|
+
zone: string;
|
|
670
|
+
}
|
|
671
|
+
/** `PUT` y `GET /api/partners/:partnerId/fallback-zone` (el GET también lo ve el dueño). */
|
|
672
|
+
export interface FallbackZone {
|
|
673
|
+
zone: string;
|
|
674
|
+
hostedZoneId: string;
|
|
675
|
+
/** Los NS de Route 53 que hay que delegar (NS de `zone` en la zona padre). */
|
|
676
|
+
nameservers: string[];
|
|
677
|
+
/** El DNS público ya responde con nuestros NS: los dominios de respaldo se pueden verificar. */
|
|
678
|
+
delegated: boolean;
|
|
679
|
+
/** Los NS que hoy ve el DNS público para `zone` (vacío = todavía no hay delegación). */
|
|
680
|
+
observedNameservers: string[];
|
|
681
|
+
/** Primera vez que se vio la delegación. */
|
|
682
|
+
delegatedAt: string | null;
|
|
683
|
+
}
|
|
684
|
+
export interface PartnerCredential {
|
|
685
|
+
keyId: string;
|
|
686
|
+
label: string;
|
|
687
|
+
createdAt: string;
|
|
688
|
+
rotatedAt: string | null;
|
|
689
|
+
/** Hasta cuándo sigue firmando el secreto anterior tras rotar. */
|
|
690
|
+
prevValidUntil: string | null;
|
|
691
|
+
revokedAt: string | null;
|
|
692
|
+
lastUsedAt: string | null;
|
|
693
|
+
}
|
|
694
|
+
/** `GET /api/partners/:partnerId/credentials` */
|
|
695
|
+
export interface ListCredentialsResponse {
|
|
696
|
+
credentials: PartnerCredential[];
|
|
697
|
+
}
|
|
698
|
+
/** `POST /api/partners/:partnerId/credentials` — 201; el secreto `mms_…` se ve una vez. */
|
|
699
|
+
export interface CreateCredentialRequest {
|
|
700
|
+
label?: string;
|
|
701
|
+
}
|
|
702
|
+
export type CreateCredentialResponse = PartnerCredential & {
|
|
703
|
+
secret: string;
|
|
704
|
+
};
|
|
705
|
+
/** `POST /api/partners/:partnerId/credentials/:keyId/rotate` — gracia 0–10080 min (default 60). */
|
|
706
|
+
export interface RotateCredentialRequest {
|
|
707
|
+
graceMinutes?: number;
|
|
708
|
+
}
|
|
709
|
+
export type RotateCredentialResponse = PartnerCredential & {
|
|
710
|
+
secret: string;
|
|
711
|
+
};
|
|
712
|
+
/** `DELETE /api/partners/:partnerId/credentials/:keyId` — inmediato, sin vuelta. */
|
|
713
|
+
export type RevokeCredentialResponse = PartnerCredential;
|
|
714
|
+
/** Rutas firmadas, relativas a `PARTNER_API_PREFIX`. `:x` = parámetro codificado con encodeURIComponent. */
|
|
715
|
+
export declare const PARTNER_ROUTES: {
|
|
716
|
+
readonly putTenant: {
|
|
717
|
+
readonly method: "PUT";
|
|
718
|
+
readonly path: "/tenants/:externalId";
|
|
719
|
+
};
|
|
720
|
+
readonly listTenants: {
|
|
721
|
+
readonly method: "GET";
|
|
722
|
+
readonly path: "/tenants";
|
|
723
|
+
};
|
|
724
|
+
readonly getTenant: {
|
|
725
|
+
readonly method: "GET";
|
|
726
|
+
readonly path: "/tenants/:externalId";
|
|
727
|
+
};
|
|
728
|
+
readonly deleteTenant: {
|
|
729
|
+
readonly method: "DELETE";
|
|
730
|
+
readonly path: "/tenants/:externalId";
|
|
731
|
+
};
|
|
732
|
+
readonly pauseTenant: {
|
|
733
|
+
readonly method: "POST";
|
|
734
|
+
readonly path: "/tenants/:externalId/pause";
|
|
735
|
+
};
|
|
736
|
+
readonly resumeTenant: {
|
|
737
|
+
readonly method: "POST";
|
|
738
|
+
readonly path: "/tenants/:externalId/resume";
|
|
739
|
+
};
|
|
740
|
+
readonly addDomain: {
|
|
741
|
+
readonly method: "POST";
|
|
742
|
+
readonly path: "/tenants/:externalId/domains";
|
|
743
|
+
};
|
|
744
|
+
readonly getDomain: {
|
|
745
|
+
readonly method: "GET";
|
|
746
|
+
readonly path: "/tenants/:externalId/domains/:domain";
|
|
747
|
+
};
|
|
748
|
+
readonly send: {
|
|
749
|
+
readonly method: "POST";
|
|
750
|
+
readonly path: "/tenants/:externalId/send";
|
|
751
|
+
};
|
|
752
|
+
readonly batch: {
|
|
753
|
+
readonly method: "POST";
|
|
754
|
+
readonly path: "/tenants/:externalId/batch";
|
|
755
|
+
};
|
|
756
|
+
readonly getBatch: {
|
|
757
|
+
readonly method: "GET";
|
|
758
|
+
readonly path: "/tenants/:externalId/batch/:batchId";
|
|
759
|
+
};
|
|
760
|
+
readonly usage: {
|
|
761
|
+
readonly method: "GET";
|
|
762
|
+
readonly path: "/usage";
|
|
763
|
+
};
|
|
764
|
+
readonly balance: {
|
|
765
|
+
readonly method: "GET";
|
|
766
|
+
readonly path: "/balance";
|
|
767
|
+
};
|
|
768
|
+
readonly balanceMovements: {
|
|
769
|
+
readonly method: "GET";
|
|
770
|
+
readonly path: "/balance/movements";
|
|
771
|
+
};
|
|
772
|
+
readonly topup: {
|
|
773
|
+
readonly method: "POST";
|
|
774
|
+
readonly path: "/topup";
|
|
775
|
+
};
|
|
776
|
+
readonly getWebhook: {
|
|
777
|
+
readonly method: "GET";
|
|
778
|
+
readonly path: "/webhook";
|
|
779
|
+
};
|
|
780
|
+
readonly setWebhook: {
|
|
781
|
+
readonly method: "PUT";
|
|
782
|
+
readonly path: "/webhook";
|
|
783
|
+
};
|
|
784
|
+
readonly testWebhook: {
|
|
785
|
+
readonly method: "POST";
|
|
786
|
+
readonly path: "/webhook/test";
|
|
787
|
+
};
|
|
788
|
+
readonly rotateWebhookSecret: {
|
|
789
|
+
readonly method: "POST";
|
|
790
|
+
readonly path: "/webhook/rotate-secret";
|
|
791
|
+
};
|
|
792
|
+
};
|
|
793
|
+
/** Rutas de administración (sin firma: sesión o `Authorization: Bearer mk_…`). Absolutas. */
|
|
794
|
+
export declare const PARTNER_ADMIN_ROUTES: {
|
|
795
|
+
readonly listPartners: {
|
|
796
|
+
readonly method: "GET";
|
|
797
|
+
readonly path: "/api/partners";
|
|
798
|
+
};
|
|
799
|
+
readonly createPartner: {
|
|
800
|
+
readonly method: "POST";
|
|
801
|
+
readonly path: "/api/partners";
|
|
802
|
+
};
|
|
803
|
+
readonly setReputationPolicy: {
|
|
804
|
+
readonly method: "PUT";
|
|
805
|
+
readonly path: "/api/partners/:partnerId/reputation-policy";
|
|
806
|
+
};
|
|
807
|
+
/** Sólo MailMask (admin): levanta cualquier pausa, incluida la de reputación. */
|
|
808
|
+
readonly resumeTenant: {
|
|
809
|
+
readonly method: "POST";
|
|
810
|
+
readonly path: "/api/partners/:partnerId/tenants/:externalId/resume";
|
|
811
|
+
};
|
|
812
|
+
/** Sólo MailMask (admin): crea o adopta la zona de respaldo y devuelve los NS a delegar. */
|
|
813
|
+
readonly setFallbackZone: {
|
|
814
|
+
readonly method: "PUT";
|
|
815
|
+
readonly path: "/api/partners/:partnerId/fallback-zone";
|
|
816
|
+
};
|
|
817
|
+
/** La zona de respaldo y si la delegación ya se ve en el DNS público. */
|
|
818
|
+
readonly getFallbackZone: {
|
|
819
|
+
readonly method: "GET";
|
|
820
|
+
readonly path: "/api/partners/:partnerId/fallback-zone";
|
|
821
|
+
};
|
|
822
|
+
/** El saldo del partner (dueño o MailMask). */
|
|
823
|
+
readonly getBalance: {
|
|
824
|
+
readonly method: "GET";
|
|
825
|
+
readonly path: "/api/partners/:partnerId/balance";
|
|
826
|
+
};
|
|
827
|
+
/** Sólo MailMask (admin): abono o cargo manual (`adjust`). */
|
|
828
|
+
readonly adjustBalance: {
|
|
829
|
+
readonly method: "POST";
|
|
830
|
+
readonly path: "/api/partners/:partnerId/balance/adjust";
|
|
831
|
+
};
|
|
832
|
+
/** Sólo MailMask (admin): prende o apaga el cobro por adelantado. */
|
|
833
|
+
readonly setPrepaidEnforced: {
|
|
834
|
+
readonly method: "PUT";
|
|
835
|
+
readonly path: "/api/partners/:partnerId/prepaid";
|
|
836
|
+
};
|
|
837
|
+
/** Recarga con MercadoPago desde la sesión del dueño (misma respuesta que `POST /topup`). */
|
|
838
|
+
readonly topup: {
|
|
839
|
+
readonly method: "POST";
|
|
840
|
+
readonly path: "/api/partners/:partnerId/topup";
|
|
841
|
+
};
|
|
842
|
+
readonly listCredentials: {
|
|
843
|
+
readonly method: "GET";
|
|
844
|
+
readonly path: "/api/partners/:partnerId/credentials";
|
|
845
|
+
};
|
|
846
|
+
readonly createCredential: {
|
|
847
|
+
readonly method: "POST";
|
|
848
|
+
readonly path: "/api/partners/:partnerId/credentials";
|
|
849
|
+
};
|
|
850
|
+
readonly rotateCredential: {
|
|
851
|
+
readonly method: "POST";
|
|
852
|
+
readonly path: "/api/partners/:partnerId/credentials/:keyId/rotate";
|
|
853
|
+
};
|
|
854
|
+
readonly revokeCredential: {
|
|
855
|
+
readonly method: "DELETE";
|
|
856
|
+
readonly path: "/api/partners/:partnerId/credentials/:keyId";
|
|
857
|
+
};
|
|
858
|
+
};
|