@peable.to/contracts 6.2.0 → 6.3.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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.reportingRoutes = exports.downloadReportQuery = exports.listReportRunsQuery = exports.createReportRunParams = exports.reportRunSchema = exports.reportRunParametersSchema = exports.reportTypeSchema = void 0;
3
+ exports.reportingRoutes = exports.downloadReportQuery = exports.listReportSchedulesQuery = exports.reportScheduleActionParams = exports.updateReportScheduleParams = exports.createReportScheduleParams = exports.listReportRunsQuery = exports.createReportRunParams = exports.reportScheduleSchema = exports.reportScheduleCadenceSchema = exports.reportRunSchema = exports.reportRunParametersSchema = exports.reportTypeSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const objects_1 = require("../../common/objects");
6
6
  const envelopes_1 = require("../../common/envelopes");
@@ -88,6 +88,7 @@ exports.reportRunSchema = (0, objects_2.defineObject)('reporting.report_run', 'R
88
88
  .nullable()
89
89
  .describe('The CSV once `succeeded` — a File of purpose `finance_report_run` (`GET /v1/files/{id}/contents` reads it).'),
90
90
  succeeded_at: primitives_1.unixSeconds.nullable(),
91
+ report_schedule: (0, objects_1.expandable)('reporting.report_schedule', 'The schedule that fired this run (`frs_…`); `null` for a run asked for directly.').nullable(),
91
92
  })
92
93
  .strict(), {
93
94
  examples: [
@@ -102,6 +103,78 @@ exports.reportRunSchema = (0, objects_2.defineObject)('reporting.report_run', 'R
102
103
  error: null,
103
104
  result: null,
104
105
  succeeded_at: null,
106
+ report_schedule: null,
107
+ },
108
+ ],
109
+ });
110
+ /** When a schedule fires (Stripe's payout-schedule spelling). */
111
+ exports.reportScheduleCadenceSchema = (0, objects_2.defineComponent)('ReportScheduleCadence', zod_1.z
112
+ .object({
113
+ interval: zod_1.z.enum(reporting_1.REPORT_SCHEDULE_INTERVALS),
114
+ weekly_anchor: zod_1.z
115
+ .enum(reporting_1.REPORT_SCHEDULE_WEEKDAYS)
116
+ .nullable()
117
+ .describe('The day a `weekly` schedule fires; `null` otherwise.'),
118
+ monthly_anchor: zod_1.z
119
+ .number()
120
+ .int()
121
+ .nullable()
122
+ .describe('The day of the month a `monthly` schedule fires (1–31; a shorter month fires on its last day); `null` otherwise.'),
123
+ })
124
+ .strict());
125
+ /**
126
+ * `reporting.report_schedule` (`frs_…`): a report run on a cadence. Each
127
+ * occurrence is local midnight in `timezone`; it creates an ordinary report
128
+ * run over the time since the previous occurrence, and emails `recipients` a
129
+ * link to it in Orbit once it succeeded (or failed). The CSV is never attached
130
+ * and never public: a recipient signs in to Orbit to download it.
131
+ */
132
+ exports.reportScheduleSchema = (0, objects_2.defineObject)('reporting.report_schedule', 'ReportSchedule', zod_1.z
133
+ .object({
134
+ id: zod_1.z.string(),
135
+ object: zod_1.z.literal('reporting.report_schedule'),
136
+ livemode: primitives_1.livemode,
137
+ created: primitives_1.unixSeconds,
138
+ report_type: reportTypeId,
139
+ parameters: zod_1.z
140
+ .object({
141
+ columns: zod_1.z.array(zod_1.z.string()).optional(),
142
+ currency: primitives_1.currency.optional(),
143
+ })
144
+ .strict()
145
+ .describe("Every run's `parameters` but the interval, which each occurrence sets: the time since the previous occurrence."),
146
+ schedule: exports.reportScheduleCadenceSchema,
147
+ timezone: zod_1.z
148
+ .string()
149
+ .describe('The IANA time zone whose midnight each occurrence is (`Europe/Madrid`).'),
150
+ recipients: zod_1.z
151
+ .array(zod_1.z.string())
152
+ .describe('Who is emailed when a run of this schedule succeeds or fails.'),
153
+ locale: zod_1.z.enum(reporting_1.REPORT_SCHEDULE_LOCALES).describe('The language of those emails.'),
154
+ status: zod_1.z.enum(reporting_1.REPORT_SCHEDULE_STATUSES),
155
+ next_run_at: primitives_1.unixSeconds
156
+ .nullable()
157
+ .describe('The next occurrence while `active`; `null` while paused or canceled.'),
158
+ last_run: (0, objects_1.expandable)('reporting.report_run', 'The newest run this schedule fired.').nullable(),
159
+ canceled_at: primitives_1.unixSeconds.nullable(),
160
+ })
161
+ .strict(), {
162
+ examples: [
163
+ {
164
+ id: 'frs_0193b8f34d1e4a6b8c27',
165
+ object: 'reporting.report_schedule',
166
+ livemode: false,
167
+ created: 1793750400,
168
+ report_type: 'balance.summary.1',
169
+ parameters: {},
170
+ schedule: { interval: 'monthly', weekly_anchor: null, monthly_anchor: 1 },
171
+ timezone: 'Europe/Madrid',
172
+ recipients: ['finance@example.com'],
173
+ locale: 'en',
174
+ status: 'active',
175
+ next_run_at: 1796079600,
176
+ last_run: null,
177
+ canceled_at: null,
105
178
  },
106
179
  ],
107
180
  });
@@ -125,7 +198,79 @@ exports.createReportRunParams = zod_1.z
125
198
  .strict(),
126
199
  })
127
200
  .strict();
128
- exports.listReportRunsQuery = (0, params_1.listQuery)({ report_type: reportTypeId.optional() });
201
+ exports.listReportRunsQuery = (0, params_1.listQuery)({
202
+ report_type: reportTypeId.optional(),
203
+ report_schedule: zod_1.z
204
+ .string()
205
+ .min(1)
206
+ .max(64)
207
+ .optional()
208
+ .describe('Only the runs this schedule (`frs_…`) fired.'),
209
+ });
210
+ /** A schedule's run template: a run's `parameters` without the interval. */
211
+ const scheduleParameters = zod_1.z
212
+ .object({
213
+ columns: zod_1.z
214
+ .array(column)
215
+ .min(1)
216
+ .max(64)
217
+ .optional()
218
+ .describe("Columns of the report type's layout, each once."),
219
+ currency: zod_1.z.enum(money_1.CURRENCY_CODES).optional(),
220
+ })
221
+ .strict();
222
+ const cadenceParams = zod_1.z
223
+ .object({
224
+ interval: zod_1.z.enum(reporting_1.REPORT_SCHEDULE_INTERVALS),
225
+ weekly_anchor: zod_1.z
226
+ .enum(reporting_1.REPORT_SCHEDULE_WEEKDAYS)
227
+ .optional()
228
+ .describe('Required for `weekly`, refused otherwise.'),
229
+ monthly_anchor: zod_1.z
230
+ .number()
231
+ .int()
232
+ .min(1)
233
+ .max(31)
234
+ .optional()
235
+ .describe('Required for `monthly`, refused otherwise; a shorter month fires on its last day.'),
236
+ })
237
+ .strict();
238
+ const recipientsParam = zod_1.z
239
+ .array(zod_1.z.email().max(254))
240
+ .max(reporting_1.REPORT_SCHEDULE_MAX_RECIPIENTS)
241
+ .describe(`At most ${String(reporting_1.REPORT_SCHEDULE_MAX_RECIPIENTS)} addresses, each once. They receive a link to the run in Orbit, never the file.`);
242
+ const timezoneParam = zod_1.z
243
+ .string()
244
+ .min(1)
245
+ .max(64)
246
+ .describe('An IANA time zone (`Europe/Madrid`); `UTC` when absent.');
247
+ /** `POST /v1/reporting/report_schedules`. */
248
+ exports.createReportScheduleParams = zod_1.z
249
+ .object({
250
+ report_type: reportTypeId,
251
+ parameters: scheduleParameters.optional(),
252
+ schedule: cadenceParams,
253
+ timezone: timezoneParam.optional(),
254
+ recipients: recipientsParam.optional(),
255
+ locale: zod_1.z.enum(reporting_1.REPORT_SCHEDULE_LOCALES).optional().describe('`en` when absent.'),
256
+ })
257
+ .strict();
258
+ /** `POST /v1/reporting/report_schedules/{id}`: each field sent replaces the stored one. */
259
+ exports.updateReportScheduleParams = zod_1.z
260
+ .object({
261
+ report_type: reportTypeId.optional(),
262
+ parameters: scheduleParameters.optional(),
263
+ schedule: cadenceParams.optional(),
264
+ timezone: timezoneParam.optional(),
265
+ recipients: recipientsParam.optional(),
266
+ locale: zod_1.z.enum(reporting_1.REPORT_SCHEDULE_LOCALES).optional(),
267
+ })
268
+ .strict();
269
+ /** Pause, resume and cancel take no parameters. */
270
+ exports.reportScheduleActionParams = zod_1.z.object({}).strict();
271
+ exports.listReportSchedulesQuery = (0, params_1.listQuery)({
272
+ status: zod_1.z.enum(reporting_1.REPORT_SCHEDULE_STATUSES).optional(),
273
+ });
129
274
  /**
130
275
  * A download's query: a run's `parameters`, spelled as a query string
131
276
  * (`columns[]=a&columns[]=b`, or one `columns=a`).
@@ -150,6 +295,7 @@ exports.downloadReportQuery = zod_1.z
150
295
  })
151
296
  .strict();
152
297
  const reportTypeIdParam = { id: 'The report type (`payments.itemized.1`, …).' };
298
+ const reportScheduleIdParam = { id: 'The report schedule (`frs_…`).' };
153
299
  exports.reportingRoutes = [
154
300
  (0, route_1.defineRoute)({
155
301
  operationId: 'listReportTypes',
@@ -232,4 +378,106 @@ exports.reportingRoutes = [
232
378
  sdk: 'reporting.reportRuns.retrieve',
233
379
  expandable: 'reporting.report_run',
234
380
  }),
381
+ (0, route_1.defineRoute)({
382
+ operationId: 'createReportSchedule',
383
+ method: 'post',
384
+ path: '/v1/reporting/report_schedules',
385
+ tag: 'Reporting',
386
+ summary: 'Create a report schedule',
387
+ description: 'A report run at local midnight in `timezone` every day, week or month, each over the time since the previous occurrence. `next_run_at` is the first occurrence after now; nothing is backfilled.',
388
+ auth: 'service',
389
+ scope: 'payments:write',
390
+ idempotent: true,
391
+ body: exports.createReportScheduleParams,
392
+ response: exports.reportScheduleSchema,
393
+ sdk: 'reporting.reportSchedules.create',
394
+ }),
395
+ (0, route_1.defineRoute)({
396
+ operationId: 'listReportSchedules',
397
+ method: 'get',
398
+ path: '/v1/reporting/report_schedules',
399
+ tag: 'Reporting',
400
+ summary: 'List report schedules',
401
+ description: 'Newest first; a canceled schedule is listed too.',
402
+ auth: 'service',
403
+ scope: 'payments:read',
404
+ query: exports.listReportSchedulesQuery,
405
+ response: (0, envelopes_1.listOf)(exports.reportScheduleSchema),
406
+ sdk: 'reporting.reportSchedules.list',
407
+ expandable: 'reporting.report_schedule',
408
+ }),
409
+ (0, route_1.defineRoute)({
410
+ operationId: 'retrieveReportSchedule',
411
+ method: 'get',
412
+ path: '/v1/reporting/report_schedules/{id}',
413
+ tag: 'Reporting',
414
+ summary: 'Retrieve a report schedule',
415
+ auth: 'service',
416
+ scope: 'payments:read',
417
+ params: reportScheduleIdParam,
418
+ query: params_1.expandOnlyQuery,
419
+ response: exports.reportScheduleSchema,
420
+ sdk: 'reporting.reportSchedules.retrieve',
421
+ expandable: 'reporting.report_schedule',
422
+ }),
423
+ (0, route_1.defineRoute)({
424
+ operationId: 'updateReportSchedule',
425
+ method: 'post',
426
+ path: '/v1/reporting/report_schedules/{id}',
427
+ tag: 'Reporting',
428
+ summary: 'Update a report schedule',
429
+ description: 'A new `schedule` or `timezone` moves an active schedule to its first occurrence after now. A canceled schedule cannot change (409 `report_schedule_unexpected_state`).',
430
+ auth: 'service',
431
+ scope: 'payments:write',
432
+ idempotent: true,
433
+ params: reportScheduleIdParam,
434
+ body: exports.updateReportScheduleParams,
435
+ response: exports.reportScheduleSchema,
436
+ sdk: 'reporting.reportSchedules.update',
437
+ }),
438
+ (0, route_1.defineRoute)({
439
+ operationId: 'pauseReportSchedule',
440
+ method: 'post',
441
+ path: '/v1/reporting/report_schedules/{id}/pause',
442
+ tag: 'Reporting',
443
+ summary: 'Pause a report schedule',
444
+ description: 'Nothing fires until it is resumed. Pausing a paused schedule answers it as it is.',
445
+ auth: 'service',
446
+ scope: 'payments:write',
447
+ idempotent: true,
448
+ params: reportScheduleIdParam,
449
+ body: exports.reportScheduleActionParams,
450
+ response: exports.reportScheduleSchema,
451
+ sdk: 'reporting.reportSchedules.pause',
452
+ }),
453
+ (0, route_1.defineRoute)({
454
+ operationId: 'resumeReportSchedule',
455
+ method: 'post',
456
+ path: '/v1/reporting/report_schedules/{id}/resume',
457
+ tag: 'Reporting',
458
+ summary: 'Resume a paused report schedule',
459
+ description: 'It fires again from its first occurrence after now; the occurrences it missed while paused are not run.',
460
+ auth: 'service',
461
+ scope: 'payments:write',
462
+ idempotent: true,
463
+ params: reportScheduleIdParam,
464
+ body: exports.reportScheduleActionParams,
465
+ response: exports.reportScheduleSchema,
466
+ sdk: 'reporting.reportSchedules.resume',
467
+ }),
468
+ (0, route_1.defineRoute)({
469
+ operationId: 'cancelReportSchedule',
470
+ method: 'post',
471
+ path: '/v1/reporting/report_schedules/{id}/cancel',
472
+ tag: 'Reporting',
473
+ summary: 'Cancel a report schedule',
474
+ description: 'For good: it never fires again, and a run it fires no longer emails anyone. Its runs and their files stay.',
475
+ auth: 'service',
476
+ scope: 'payments:write',
477
+ idempotent: true,
478
+ params: reportScheduleIdParam,
479
+ body: exports.reportScheduleActionParams,
480
+ response: exports.reportScheduleSchema,
481
+ sdk: 'reporting.reportSchedules.cancel',
482
+ }),
235
483
  ];
@@ -122,6 +122,7 @@ exports.ERROR_CODE_DESCRIPTIONS = {
122
122
  quote_expired: 'The quote expired.',
123
123
  invoice_payment_intent_requires_action: "The invoice's payment needs the customer to act (a card challenge).",
124
124
  subscription_unexpected_state: 'The subscription is in a status that does not allow this operation.',
125
+ report_schedule_unexpected_state: 'A canceled report schedule never changes.',
125
126
  customer_currency_mismatch: "The currency differs from the customer's billing currency.",
126
127
  customer_test_clock_mismatch: 'The objects live on different test clocks.',
127
128
  coupon_invalid: 'The coupon does not exist or is no longer valid.',
@@ -3,7 +3,7 @@
3
3
  // run's lifecycle, and the methods and status classes a request log is
4
4
  // filtered by. Zod-free, like every vocabulary module.
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.REQUEST_LOG_RETENTION_DAYS = exports.REQUEST_LOG_STATUS_CLASSES = exports.REQUEST_LOG_METHODS = exports.REPORT_RUN_STATUSES = exports.REPORT_TYPE_IDS = void 0;
6
+ exports.REPORT_SCHEDULE_MAX_RECIPIENTS = exports.REPORT_SCHEDULE_LOCALES = exports.REPORT_SCHEDULE_WEEKDAYS = exports.REPORT_SCHEDULE_INTERVALS = exports.REPORT_SCHEDULE_STATUSES = exports.REQUEST_LOG_RETENTION_DAYS = exports.REQUEST_LOG_STATUS_CLASSES = exports.REQUEST_LOG_METHODS = exports.REPORT_RUN_STATUSES = exports.REPORT_TYPE_IDS = void 0;
7
7
  /**
8
8
  * Every report Peable produces, by Stripe's versioned id: the trailing `.1` is
9
9
  * the column layout's version, so a layout can change under a new id without
@@ -25,3 +25,30 @@ exports.REQUEST_LOG_METHODS = ['GET', 'POST', 'DELETE', 'PATCH', 'PUT'];
25
25
  exports.REQUEST_LOG_STATUS_CLASSES = ['2xx', '3xx', '4xx', '5xx'];
26
26
  /** How long a request log is kept (and served). */
27
27
  exports.REQUEST_LOG_RETENTION_DAYS = 30;
28
+ /**
29
+ * A report schedule's lifecycle: `active` fires at each occurrence, `paused`
30
+ * fires nothing until resumed (resuming never backfills), `canceled` is
31
+ * terminal.
32
+ */
33
+ exports.REPORT_SCHEDULE_STATUSES = ['active', 'paused', 'canceled'];
34
+ /**
35
+ * How often a schedule fires — Stripe's payout-schedule spelling: every day,
36
+ * once a week on `weekly_anchor`, once a month on `monthly_anchor`. Each
37
+ * occurrence is local midnight in the schedule's timezone, and its run covers
38
+ * the time since the previous occurrence.
39
+ */
40
+ exports.REPORT_SCHEDULE_INTERVALS = ['daily', 'weekly', 'monthly'];
41
+ /** A weekly schedule's day, Monday first (ISO 8601). */
42
+ exports.REPORT_SCHEDULE_WEEKDAYS = [
43
+ 'monday',
44
+ 'tuesday',
45
+ 'wednesday',
46
+ 'thursday',
47
+ 'friday',
48
+ 'saturday',
49
+ 'sunday',
50
+ ];
51
+ /** The languages a schedule's emails are written in — Orbit's own. */
52
+ exports.REPORT_SCHEDULE_LOCALES = ['en', 'es'];
53
+ /** How many addresses one schedule may email. */
54
+ exports.REPORT_SCHEDULE_MAX_RECIPIENTS = 10;
@@ -1,12 +1,12 @@
1
1
  import { z } from 'zod';
2
- import { embedded } from '../../common/objects.js';
2
+ import { embedded, expandable } from '../../common/objects.js';
3
3
  import { listOf } from '../../common/envelopes.js';
4
4
  import { defineComponent, defineObject, describe } from '../../common/objects.js';
5
5
  import { emptyQuery, expandOnlyQuery, listQuery, queryInteger, unixSecondsParam, } from '../../common/params.js';
6
6
  import { currency, livemode, unixSeconds } from '../../common/primitives.js';
7
7
  import { defineRoute } from '../../route.js';
8
8
  import { CURRENCY_CODES } from '../../vocabulary/money.js';
9
- import { REPORT_RUN_STATUSES, REPORT_TYPE_IDS } from '../../vocabulary/reporting.js';
9
+ import { REPORT_RUN_STATUSES, REPORT_SCHEDULE_INTERVALS, REPORT_SCHEDULE_LOCALES, REPORT_SCHEDULE_MAX_RECIPIENTS, REPORT_SCHEDULE_STATUSES, REPORT_SCHEDULE_WEEKDAYS, REPORT_TYPE_IDS, } from '../../vocabulary/reporting.js';
10
10
  /**
11
11
  * Reporting (Stripe's `reporting.*`): a merchant asks for a report over an
12
12
  * interval, Peable computes it in the background and stores the CSV as a
@@ -85,6 +85,7 @@ export const reportRunSchema = defineObject('reporting.report_run', 'ReportRun',
85
85
  .nullable()
86
86
  .describe('The CSV once `succeeded` — a File of purpose `finance_report_run` (`GET /v1/files/{id}/contents` reads it).'),
87
87
  succeeded_at: unixSeconds.nullable(),
88
+ report_schedule: expandable('reporting.report_schedule', 'The schedule that fired this run (`frs_…`); `null` for a run asked for directly.').nullable(),
88
89
  })
89
90
  .strict(), {
90
91
  examples: [
@@ -99,6 +100,78 @@ export const reportRunSchema = defineObject('reporting.report_run', 'ReportRun',
99
100
  error: null,
100
101
  result: null,
101
102
  succeeded_at: null,
103
+ report_schedule: null,
104
+ },
105
+ ],
106
+ });
107
+ /** When a schedule fires (Stripe's payout-schedule spelling). */
108
+ export const reportScheduleCadenceSchema = defineComponent('ReportScheduleCadence', z
109
+ .object({
110
+ interval: z.enum(REPORT_SCHEDULE_INTERVALS),
111
+ weekly_anchor: z
112
+ .enum(REPORT_SCHEDULE_WEEKDAYS)
113
+ .nullable()
114
+ .describe('The day a `weekly` schedule fires; `null` otherwise.'),
115
+ monthly_anchor: z
116
+ .number()
117
+ .int()
118
+ .nullable()
119
+ .describe('The day of the month a `monthly` schedule fires (1–31; a shorter month fires on its last day); `null` otherwise.'),
120
+ })
121
+ .strict());
122
+ /**
123
+ * `reporting.report_schedule` (`frs_…`): a report run on a cadence. Each
124
+ * occurrence is local midnight in `timezone`; it creates an ordinary report
125
+ * run over the time since the previous occurrence, and emails `recipients` a
126
+ * link to it in Orbit once it succeeded (or failed). The CSV is never attached
127
+ * and never public: a recipient signs in to Orbit to download it.
128
+ */
129
+ export const reportScheduleSchema = defineObject('reporting.report_schedule', 'ReportSchedule', z
130
+ .object({
131
+ id: z.string(),
132
+ object: z.literal('reporting.report_schedule'),
133
+ livemode,
134
+ created: unixSeconds,
135
+ report_type: reportTypeId,
136
+ parameters: z
137
+ .object({
138
+ columns: z.array(z.string()).optional(),
139
+ currency: currency.optional(),
140
+ })
141
+ .strict()
142
+ .describe("Every run's `parameters` but the interval, which each occurrence sets: the time since the previous occurrence."),
143
+ schedule: reportScheduleCadenceSchema,
144
+ timezone: z
145
+ .string()
146
+ .describe('The IANA time zone whose midnight each occurrence is (`Europe/Madrid`).'),
147
+ recipients: z
148
+ .array(z.string())
149
+ .describe('Who is emailed when a run of this schedule succeeds or fails.'),
150
+ locale: z.enum(REPORT_SCHEDULE_LOCALES).describe('The language of those emails.'),
151
+ status: z.enum(REPORT_SCHEDULE_STATUSES),
152
+ next_run_at: unixSeconds
153
+ .nullable()
154
+ .describe('The next occurrence while `active`; `null` while paused or canceled.'),
155
+ last_run: expandable('reporting.report_run', 'The newest run this schedule fired.').nullable(),
156
+ canceled_at: unixSeconds.nullable(),
157
+ })
158
+ .strict(), {
159
+ examples: [
160
+ {
161
+ id: 'frs_0193b8f34d1e4a6b8c27',
162
+ object: 'reporting.report_schedule',
163
+ livemode: false,
164
+ created: 1793750400,
165
+ report_type: 'balance.summary.1',
166
+ parameters: {},
167
+ schedule: { interval: 'monthly', weekly_anchor: null, monthly_anchor: 1 },
168
+ timezone: 'Europe/Madrid',
169
+ recipients: ['finance@example.com'],
170
+ locale: 'en',
171
+ status: 'active',
172
+ next_run_at: 1796079600,
173
+ last_run: null,
174
+ canceled_at: null,
102
175
  },
103
176
  ],
104
177
  });
@@ -122,7 +195,79 @@ export const createReportRunParams = z
122
195
  .strict(),
123
196
  })
124
197
  .strict();
125
- export const listReportRunsQuery = listQuery({ report_type: reportTypeId.optional() });
198
+ export const listReportRunsQuery = listQuery({
199
+ report_type: reportTypeId.optional(),
200
+ report_schedule: z
201
+ .string()
202
+ .min(1)
203
+ .max(64)
204
+ .optional()
205
+ .describe('Only the runs this schedule (`frs_…`) fired.'),
206
+ });
207
+ /** A schedule's run template: a run's `parameters` without the interval. */
208
+ const scheduleParameters = z
209
+ .object({
210
+ columns: z
211
+ .array(column)
212
+ .min(1)
213
+ .max(64)
214
+ .optional()
215
+ .describe("Columns of the report type's layout, each once."),
216
+ currency: z.enum(CURRENCY_CODES).optional(),
217
+ })
218
+ .strict();
219
+ const cadenceParams = z
220
+ .object({
221
+ interval: z.enum(REPORT_SCHEDULE_INTERVALS),
222
+ weekly_anchor: z
223
+ .enum(REPORT_SCHEDULE_WEEKDAYS)
224
+ .optional()
225
+ .describe('Required for `weekly`, refused otherwise.'),
226
+ monthly_anchor: z
227
+ .number()
228
+ .int()
229
+ .min(1)
230
+ .max(31)
231
+ .optional()
232
+ .describe('Required for `monthly`, refused otherwise; a shorter month fires on its last day.'),
233
+ })
234
+ .strict();
235
+ const recipientsParam = z
236
+ .array(z.email().max(254))
237
+ .max(REPORT_SCHEDULE_MAX_RECIPIENTS)
238
+ .describe(`At most ${String(REPORT_SCHEDULE_MAX_RECIPIENTS)} addresses, each once. They receive a link to the run in Orbit, never the file.`);
239
+ const timezoneParam = z
240
+ .string()
241
+ .min(1)
242
+ .max(64)
243
+ .describe('An IANA time zone (`Europe/Madrid`); `UTC` when absent.');
244
+ /** `POST /v1/reporting/report_schedules`. */
245
+ export const createReportScheduleParams = z
246
+ .object({
247
+ report_type: reportTypeId,
248
+ parameters: scheduleParameters.optional(),
249
+ schedule: cadenceParams,
250
+ timezone: timezoneParam.optional(),
251
+ recipients: recipientsParam.optional(),
252
+ locale: z.enum(REPORT_SCHEDULE_LOCALES).optional().describe('`en` when absent.'),
253
+ })
254
+ .strict();
255
+ /** `POST /v1/reporting/report_schedules/{id}`: each field sent replaces the stored one. */
256
+ export const updateReportScheduleParams = z
257
+ .object({
258
+ report_type: reportTypeId.optional(),
259
+ parameters: scheduleParameters.optional(),
260
+ schedule: cadenceParams.optional(),
261
+ timezone: timezoneParam.optional(),
262
+ recipients: recipientsParam.optional(),
263
+ locale: z.enum(REPORT_SCHEDULE_LOCALES).optional(),
264
+ })
265
+ .strict();
266
+ /** Pause, resume and cancel take no parameters. */
267
+ export const reportScheduleActionParams = z.object({}).strict();
268
+ export const listReportSchedulesQuery = listQuery({
269
+ status: z.enum(REPORT_SCHEDULE_STATUSES).optional(),
270
+ });
126
271
  /**
127
272
  * A download's query: a run's `parameters`, spelled as a query string
128
273
  * (`columns[]=a&columns[]=b`, or one `columns=a`).
@@ -147,6 +292,7 @@ export const downloadReportQuery = z
147
292
  })
148
293
  .strict();
149
294
  const reportTypeIdParam = { id: 'The report type (`payments.itemized.1`, …).' };
295
+ const reportScheduleIdParam = { id: 'The report schedule (`frs_…`).' };
150
296
  export const reportingRoutes = [
151
297
  defineRoute({
152
298
  operationId: 'listReportTypes',
@@ -229,4 +375,106 @@ export const reportingRoutes = [
229
375
  sdk: 'reporting.reportRuns.retrieve',
230
376
  expandable: 'reporting.report_run',
231
377
  }),
378
+ defineRoute({
379
+ operationId: 'createReportSchedule',
380
+ method: 'post',
381
+ path: '/v1/reporting/report_schedules',
382
+ tag: 'Reporting',
383
+ summary: 'Create a report schedule',
384
+ description: 'A report run at local midnight in `timezone` every day, week or month, each over the time since the previous occurrence. `next_run_at` is the first occurrence after now; nothing is backfilled.',
385
+ auth: 'service',
386
+ scope: 'payments:write',
387
+ idempotent: true,
388
+ body: createReportScheduleParams,
389
+ response: reportScheduleSchema,
390
+ sdk: 'reporting.reportSchedules.create',
391
+ }),
392
+ defineRoute({
393
+ operationId: 'listReportSchedules',
394
+ method: 'get',
395
+ path: '/v1/reporting/report_schedules',
396
+ tag: 'Reporting',
397
+ summary: 'List report schedules',
398
+ description: 'Newest first; a canceled schedule is listed too.',
399
+ auth: 'service',
400
+ scope: 'payments:read',
401
+ query: listReportSchedulesQuery,
402
+ response: listOf(reportScheduleSchema),
403
+ sdk: 'reporting.reportSchedules.list',
404
+ expandable: 'reporting.report_schedule',
405
+ }),
406
+ defineRoute({
407
+ operationId: 'retrieveReportSchedule',
408
+ method: 'get',
409
+ path: '/v1/reporting/report_schedules/{id}',
410
+ tag: 'Reporting',
411
+ summary: 'Retrieve a report schedule',
412
+ auth: 'service',
413
+ scope: 'payments:read',
414
+ params: reportScheduleIdParam,
415
+ query: expandOnlyQuery,
416
+ response: reportScheduleSchema,
417
+ sdk: 'reporting.reportSchedules.retrieve',
418
+ expandable: 'reporting.report_schedule',
419
+ }),
420
+ defineRoute({
421
+ operationId: 'updateReportSchedule',
422
+ method: 'post',
423
+ path: '/v1/reporting/report_schedules/{id}',
424
+ tag: 'Reporting',
425
+ summary: 'Update a report schedule',
426
+ description: 'A new `schedule` or `timezone` moves an active schedule to its first occurrence after now. A canceled schedule cannot change (409 `report_schedule_unexpected_state`).',
427
+ auth: 'service',
428
+ scope: 'payments:write',
429
+ idempotent: true,
430
+ params: reportScheduleIdParam,
431
+ body: updateReportScheduleParams,
432
+ response: reportScheduleSchema,
433
+ sdk: 'reporting.reportSchedules.update',
434
+ }),
435
+ defineRoute({
436
+ operationId: 'pauseReportSchedule',
437
+ method: 'post',
438
+ path: '/v1/reporting/report_schedules/{id}/pause',
439
+ tag: 'Reporting',
440
+ summary: 'Pause a report schedule',
441
+ description: 'Nothing fires until it is resumed. Pausing a paused schedule answers it as it is.',
442
+ auth: 'service',
443
+ scope: 'payments:write',
444
+ idempotent: true,
445
+ params: reportScheduleIdParam,
446
+ body: reportScheduleActionParams,
447
+ response: reportScheduleSchema,
448
+ sdk: 'reporting.reportSchedules.pause',
449
+ }),
450
+ defineRoute({
451
+ operationId: 'resumeReportSchedule',
452
+ method: 'post',
453
+ path: '/v1/reporting/report_schedules/{id}/resume',
454
+ tag: 'Reporting',
455
+ summary: 'Resume a paused report schedule',
456
+ description: 'It fires again from its first occurrence after now; the occurrences it missed while paused are not run.',
457
+ auth: 'service',
458
+ scope: 'payments:write',
459
+ idempotent: true,
460
+ params: reportScheduleIdParam,
461
+ body: reportScheduleActionParams,
462
+ response: reportScheduleSchema,
463
+ sdk: 'reporting.reportSchedules.resume',
464
+ }),
465
+ defineRoute({
466
+ operationId: 'cancelReportSchedule',
467
+ method: 'post',
468
+ path: '/v1/reporting/report_schedules/{id}/cancel',
469
+ tag: 'Reporting',
470
+ summary: 'Cancel a report schedule',
471
+ description: 'For good: it never fires again, and a run it fires no longer emails anyone. Its runs and their files stay.',
472
+ auth: 'service',
473
+ scope: 'payments:write',
474
+ idempotent: true,
475
+ params: reportScheduleIdParam,
476
+ body: reportScheduleActionParams,
477
+ response: reportScheduleSchema,
478
+ sdk: 'reporting.reportSchedules.cancel',
479
+ }),
232
480
  ];
@@ -117,6 +117,7 @@ export const ERROR_CODE_DESCRIPTIONS = {
117
117
  quote_expired: 'The quote expired.',
118
118
  invoice_payment_intent_requires_action: "The invoice's payment needs the customer to act (a card challenge).",
119
119
  subscription_unexpected_state: 'The subscription is in a status that does not allow this operation.',
120
+ report_schedule_unexpected_state: 'A canceled report schedule never changes.',
120
121
  customer_currency_mismatch: "The currency differs from the customer's billing currency.",
121
122
  customer_test_clock_mismatch: 'The objects live on different test clocks.',
122
123
  coupon_invalid: 'The coupon does not exist or is no longer valid.',
@@ -22,3 +22,30 @@ export const REQUEST_LOG_METHODS = ['GET', 'POST', 'DELETE', 'PATCH', 'PUT'];
22
22
  export const REQUEST_LOG_STATUS_CLASSES = ['2xx', '3xx', '4xx', '5xx'];
23
23
  /** How long a request log is kept (and served). */
24
24
  export const REQUEST_LOG_RETENTION_DAYS = 30;
25
+ /**
26
+ * A report schedule's lifecycle: `active` fires at each occurrence, `paused`
27
+ * fires nothing until resumed (resuming never backfills), `canceled` is
28
+ * terminal.
29
+ */
30
+ export const REPORT_SCHEDULE_STATUSES = ['active', 'paused', 'canceled'];
31
+ /**
32
+ * How often a schedule fires — Stripe's payout-schedule spelling: every day,
33
+ * once a week on `weekly_anchor`, once a month on `monthly_anchor`. Each
34
+ * occurrence is local midnight in the schedule's timezone, and its run covers
35
+ * the time since the previous occurrence.
36
+ */
37
+ export const REPORT_SCHEDULE_INTERVALS = ['daily', 'weekly', 'monthly'];
38
+ /** A weekly schedule's day, Monday first (ISO 8601). */
39
+ export const REPORT_SCHEDULE_WEEKDAYS = [
40
+ 'monday',
41
+ 'tuesday',
42
+ 'wednesday',
43
+ 'thursday',
44
+ 'friday',
45
+ 'saturday',
46
+ 'sunday',
47
+ ];
48
+ /** The languages a schedule's emails are written in — Orbit's own. */
49
+ export const REPORT_SCHEDULE_LOCALES = ['en', 'es'];
50
+ /** How many addresses one schedule may email. */
51
+ export const REPORT_SCHEDULE_MAX_RECIPIENTS = 10;