---
title: "Set a table's refresh cadence"
method: POST
path: "/tables/{table_id}/schedule/"
tags: ["table-schedules"]
---

# Set a table's refresh cadence

`POST /tables/{table_id}/schedule/`

## Request

- Base URL: `https://api.getoneprofile.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getoneprofile.ai/tables/{table_id}/schedule/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Re-import on a cadence. The presets are calendar statements (monthly on the same date, quarterly in the same month) while `custom` is an interval counted from when you set it up.

The response carries the next few fire times, so a cadence can be checked before it is left to run unattended.

Turning on `delete_missing_rows` makes every scheduled run re-list the whole source: a changes-only fetch cannot tell a deleted record from an unchanged one, and treating the difference as deletion would empty the table.

## Path parameters

- `table_id` string, uuid, required

## Request body

- CreateTableScheduleRequestSchema: Set up a table's refresh cadence.
  - `delete_missing_rows` boolean: Delete rows whose records have gone from the source. Forces every scheduled run to re-list everything, and is capped per run so a provider outage cannot empty the table.
  - `frequency` 'hourly' | 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'custom', required: How often a table re-imports, in the vocabulary the settings panel offers. The presets are stored as cron because "the 8th of every month at 09:00" is a calendar statement that an interval cannot make: an interval of 30 days drifts off the date within a year. CUSTOM is the escape hatch for "every N days/weeks/months", which is an interval and drifts on purpose, anchored to when the user set it up. HOURLY is cron too, and it is the one preset that ignores the hour the user picked: "every hour at 09:00" is not a thing anyone means. The minute is kept, so an hourly refresh set at 09:20 runs at twenty past every hour, and a source that rate-limits is not hit by every table on the hour.
  - `hour` integer: Local hour to run at.
  - `interval_unit` 'hours' | 'days' | 'weeks' | 'months': The unit a CUSTOM cadence counts in. HOURS is the only one with no time of day to honour: "every 6 hours at 09:00" is two statements that cannot both be true. It anchors to the next occurrence of the chosen MINUTE instead, the same way the HOURLY preset keeps the minute and drops the hour.
  - `interval_value` integer, nullable: Required for a custom cadence.
  - `minute` integer: Local minute to run at.
  - `time_zone` string, nullable: IANA zone the time is in; defaults to UTC. An unknown zone falls back to UTC.

## Response `201`

Successful Response

- TableScheduleSchema: A table's refresh cadence.
  - `created_at` string, date-time, required
  - `delete_missing_rows` boolean, required: Whether a scheduled run deletes rows whose records are gone from the source. Forces every scheduled run to re-list everything, since a changes-only fetch cannot tell a deleted record from an unchanged one.
  - `frequency` 'hourly' | 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'custom', required: How often a table re-imports, in the vocabulary the settings panel offers. The presets are stored as cron because "the 8th of every month at 09:00" is a calendar statement that an interval cannot make: an interval of 30 days drifts off the date within a year. CUSTOM is the escape hatch for "every N days/weeks/months", which is an interval and drifts on purpose, anchored to when the user set it up. HOURLY is cron too, and it is the one preset that ignores the hour the user picked: "every hour at 09:00" is not a thing anyone means. The minute is kept, so an hourly refresh set at 09:20 runs at twenty past every hour, and a source that rate-limits is not hit by every table on the hour.
  - `hour` integer, required: Local hour the cadence runs at.
  - `id` string, uuid, required
  - `interval_unit` 'hours' | 'days' | 'weeks' | 'months', required: The unit a CUSTOM cadence counts in. HOURS is the only one with no time of day to honour: "every 6 hours at 09:00" is two statements that cannot both be true. It anchors to the next occurrence of the chosen MINUTE instead, the same way the HOURLY preset keeps the minute and drops the hour.
  - `interval_value` integer, nullable, required: How many units apart, for a custom cadence.
  - `last_skip_reason` 'source_detached' | 'run_in_progress', required: Why a firing skipped starting an import instead of recording a run. Both cases advance ``next_run_at`` the same as a real run does, so without this the schedule just quietly keeps ticking, and a user watching for their nightly refresh has nothing to look at but silence.
  - `last_skipped_at` string, date-time, nullable, required: When a firing last skipped without starting a run; null if it never has.
  - `minute` integer, required: Local minute the cadence runs at.
  - `next_run_at` string, date-time, required
  - `next_runs` string[], required: The next few fire times, so a cadence can be checked before it is trusted.
  - `paused` boolean, required
  - `table_id` string, uuid, required
  - `time_zone` string, required: IANA zone the run time is expressed in.
  - `updated_at` string, date-time, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Table not found
- `422`: Invalid request made to the API.
- `500`: Internal server error.
- `502`: Bad gateway error.
- `503`: Service unavailable.

---

[API](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile.md) · [All operations](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/llms.txt) · [OpenAPI document](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/revisions/4a6bd71e3f02?raw)
