Skip to content

Platform

Everything cross-cutting: the tenant metadata a client fetches first, files, notifications, automation rules, AI, custom fields, spreadsheets and bulk edits.

279 endpoints across 20 groups. A permission written module.resource.action:own is scoped: :own covers rows that are yours, :any covers everyone’s, and :any satisfies a check for :own.

EndpointPermission
POST /ai/assist/writeai.use
POST /ai/assistantai.use
POST /ai/assistant/transcribeai.use
POST /ai/companies/{company_id}/digestai.use
GET /ai/reportsai.use
POST /ai/reportsai.use
GET /ai/reports/{report_id}ai.use
PUT /ai/reports/{report_id}ai.use
DELETE /ai/reports/{report_id}ai.use
POST /ai/reports/generateai.use
GET /ai/settingsai.settings.manage
PUT /ai/settingsai.settings.manage
DELETE /ai/settingsai.settings.manage
POST /ai/settings/modelsai.settings.manage
POST /ai/settings/testai.settings.manage
POST /ai/tasks/parseai.use
POST /ai/tasks/transcribeai.use
POST /ai/time/parseai.use
POST /ai/time/reconstructai.use
POST /ai/time/transcribeai.use
GET /ai/usageai.settings.manage
Parameters and schemas per endpoint

Writing Assist

Permission: ai.use

Request body: WritingAssistRequest

Returns: 200

Assistant

Permission: ai.use

Request body: AssistantRequest

Returns: 200

Assistant Transcribe — Speech to text for the assistant’s composer.

Permission: ai.use

Request body: AssistantTranscribeRequest

Returns: 200 · TimeTranscribeResult

POST /api/v1/ai/companies/{company_id}/digest

Section titled “POST /api/v1/ai/companies/{company_id}/digest”

Company Digest

Permission: ai.use

Path parameters: company_id

Request body: DigestRequest

Returns: 200

List Reports

Permission: ai.use

Query parameters:

NameTypeMeaning
company_idstring

Returns: 200 · ReportRead[]

Create Report

Permission: ai.use

Request body: ReportCreate

Returns: 201 · ReportRead

Get Report

Permission: ai.use

Path parameters: report_id

Returns: 200 · ReportRead

Update Report

Permission: ai.use

Path parameters: report_id

Request body: ReportUpdate

Returns: 200 · ReportRead

Delete Report

Permission: ai.use

Path parameters: report_id

Returns: 204

Generate Report

Permission: ai.use

Request body: ReportGenerateRequest

Returns: 200

Get Ai Settings

Permission: ai.settings.manage

Returns: 200 · AISettingsRead

Save Ai Settings

Permission: ai.settings.manage

Request body: AISettingsWrite

Returns: 200 · AISettingsRead

Delete Ai Settings

Permission: ai.settings.manage

Returns: 204

List Ai Models — Live model listing for the settings picker — a settings-page helper like the test button, so a provider failure comes back as data, never a 500.

Permission: ai.settings.manage

Request body: AIModelsRequest

Returns: 200 · AIModelsResult

Test Ai Settings

Permission: ai.settings.manage

Returns: 200 · AITestResult

Task Parse — One dictation into a draft task. Creates nothing (#129’s rule).

Permission: ai.use

Request body: TaskParseRequest

Returns: 200 · TaskParseResult

Task Transcribe — Speech to text for the dictated-task field (#382).

Permission: ai.use

Request body: TaskTranscribeRequest

Returns: 200 · TimeTranscribeResult

Time Parse

Permission: ai.use

Request body: TimeParseRequest

Returns: 200 · TimeParseResult

Time Reconstruct

Permission: ai.use

Request body: TimeReconstructRequest

Returns: 200 · TimeReconstructResult

Time Transcribe — Speech to text for the quick-add field (#246).

Permission: ai.use

Request body: TimeTranscribeRequest

Returns: 200 · TimeTranscribeResult

Ai Usage

Permission: ai.settings.manage

Returns: 200 · AIUsageSummary

EndpointPermission
GET /automation/catalogautomation.rule.read
POST /automation/dry-runautomation.rule.write
GET /automation/rulesautomation.rule.read
POST /automation/rulesautomation.rule.write
GET /automation/rules/{rule_id}automation.rule.read
PATCH /automation/rules/{rule_id}automation.rule.write
DELETE /automation/rules/{rule_id}automation.rule.write
GET /automation/runsautomation.run.read
Parameters and schemas per endpoint

Get Catalog — The trigger events and action types the rule editor may offer (code-defined).

Permission: automation.rule.read

Returns: 200 · CatalogRead

Dry Run — Evaluate a draft rule against a sample entity; executes nothing.

Permission: automation.rule.write

Request body: DryRunRequest

Returns: 200 · DryRunResult

List Rules

Permission: automation.rule.read

Returns: 200 · RuleRead[]

Create Rule

Permission: automation.rule.write

Request body: RuleCreate

Returns: 201 · RuleRead

Get Rule

Permission: automation.rule.read

Path parameters: rule_id

Returns: 200 · RuleRead

Update Rule

Permission: automation.rule.write

Path parameters: rule_id

Request body: RuleUpdate

Returns: 200 · RuleRead

Delete Rule

Permission: automation.rule.write

Path parameters: rule_id

Returns: 204

List Runs

Permission: automation.run.read

Query parameters:

NameTypeMeaning
rule_idstring
limitinteger(default 50)
offsetinteger(default 0)

Returns: 200 · Page_RunRead_

EndpointPermission
POST /bulk/company/deletecompanies.company.delete
POST /bulk/company/updatecompanies.company.write
POST /bulk/contact/deletecontacts.contact.delete
POST /bulk/contact/updatecontacts.contact.write
POST /bulk/domain/deletedomains.domain.delete
POST /bulk/domain/updatedomains.domain.write
POST /bulk/interaction/deleteinteractions.interaction.delete
POST /bulk/invoice/deleteinvoicing.invoice.delete
POST /bulk/project/deleteprojects.project.delete
POST /bulk/project/updateprojects.project.write
POST /bulk/subscription/deletesubscriptions.subscription.delete
POST /bulk/subscription/updatesubscriptions.subscription.write
POST /bulk/task/deletetasks.task.delete
POST /bulk/task/updatetasks.task.write
POST /bulk/uptime_monitor/deleteuptime.monitor.write
POST /bulk/uptime_monitor/updateuptime.monitor.write
POST /bulk/website/deletewebsites.website.delete
POST /bulk/website/updatewebsites.website.write
Parameters and schemas per endpoint

Bulk Delete Company — Delete a selection of company records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: companies.company.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Company — Set fields on a selection of company records: status. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: companies.company.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

Bulk Delete Contact — Delete a selection of contact records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: contacts.contact.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Contact — Set fields on a selection of contact records: company. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: contacts.contact.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

Bulk Delete Domain — Delete a selection of domain records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: domains.domain.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Domain — Set fields on a selection of domain records: status, company, registrar_provider, dns_provider, email_provider, invoiceable, next_invoice_date, billed_until, start_date, price_override. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: domains.domain.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

Bulk Delete Interaction — Delete a selection of interaction records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: interactions.interaction.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Delete Invoice — Delete a selection of invoice records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: invoicing.invoice.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Delete Project — Delete a selection of project records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: projects.project.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Project — Set fields on a selection of project records: status, company, billable_default. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: projects.project.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

Bulk Delete Subscription — Delete a selection of subscription records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: subscriptions.subscription.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Subscription — Set fields on a selection of subscription records: status, type, company, billed_until, billed_in_advance, notes_on_invoice. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: subscriptions.subscription.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

Bulk Delete Task — Delete a selection of task records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: tasks.task.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Task — Set fields on a selection of task records: status, assignee, priority, project, company, due_date. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: tasks.task.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

Bulk Delete Uptime Monitor — Delete a selection of uptime_monitor records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: uptime.monitor.write

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Uptime Monitor — Set fields on a selection of uptime_monitor records: company. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: uptime.monitor.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

Bulk Delete Website — Delete a selection of website records. Permanent, and per row: the rows the batch could do are done, and the rest come back in failed.

Permission: websites.website.delete

Request body: BulkDeleteRequest

Returns: 200 · BulkActionResult

Bulk Update Website — Set fields on a selection of website records: hosting, uptime_enabled. Keys are the entity’s own stable column keys (the ones its CSV export uses). An absent key leaves every row’s own value alone; an explicit null clears it where the field allows that. Rows are independent — an ineligible one is reported in failed, never rolled back over the rest.

Permission: websites.website.write

Request body: BulkUpdateRequest

Returns: 200 · BulkActionResult

EndpointPermission
GET /custom-fields/definitionssettings.customfields.read
POST /custom-fields/definitionssettings.customfields.write
PATCH /custom-fields/definitions/{definition_id}settings.customfields.write
DELETE /custom-fields/definitions/{definition_id}settings.customfields.write
GET /custom-fields/definitions/batchsettings.customfields.read
GET /custom-fields/entity-typessettings.customfields.read
GET /custom-fields/scopessettings.customfields.read
Parameters and schemas per endpoint

List Definitions

Permission: settings.customfields.read

Query parameters:

NameTypeMeaning
entity_typestring · required
include_inactiveboolean(default false)

Returns: 200 · CustomFieldDefinitionRead[]

Create Definition

Permission: settings.customfields.write

Request body: CustomFieldDefinitionCreate

Returns: 201 · CustomFieldDefinitionRead

PATCH /api/v1/custom-fields/definitions/{definition_id}

Section titled “PATCH /api/v1/custom-fields/definitions/{definition_id}”

Update Definition

Permission: settings.customfields.write

Path parameters: definition_id

Request body: CustomFieldDefinitionUpdate

Returns: 200 · CustomFieldDefinitionRead

DELETE /api/v1/custom-fields/definitions/{definition_id}

Section titled “DELETE /api/v1/custom-fields/definitions/{definition_id}”

Delete Definition

Permission: settings.customfields.write

Path parameters: definition_id

Returns: 204

GET /api/v1/custom-fields/definitions/batch

Section titled “GET /api/v1/custom-fields/definitions/batch”

List Definitions Batch — Several entity types’ definitions in one call, keyed by entity type.

Permission: settings.customfields.read

Query parameters:

NameTypeMeaning
entity_typestring[] · required
include_inactiveboolean(default false)

Returns: 200 · object

List Entity Types

Permission: settings.customfields.read

Returns: 200 · array

List Scopes — The dimensions a definition of entity_type may be narrowed to, with this tenant’s options for each (scoping.py). Empty for an entity type no module has registered a dimension for — which is what tells the settings screen not to draw the control (#253).

Permission: settings.customfields.read

Query parameters:

NameTypeMeaning
entity_typestring · required

Returns: 200 · CustomFieldScopeRead[]

EndpointPermission
GET /dashboard/prefsdashboard.prefs.read
PUT /dashboard/prefsdashboard.prefs.write
DELETE /dashboard/prefsdashboard.prefs.write
PUT /dashboard/prefs/defaultsettings.dashboard.manage
Parameters and schemas per endpoint

Get Prefs — The effective layout for the current user: own row → org template → none.

Permission: dashboard.prefs.read

Returns: 200 · DashboardPrefs

Set Prefs

Permission: dashboard.prefs.write

Request body: DashboardPrefsUpdate

Returns: 200 · DashboardPrefs

Reset Prefs — Drop the personal layout; the user falls back to the org template.

Permission: dashboard.prefs.write

Returns: 204

Set Default Prefs — The org-wide template that members inherit (settings.dashboard.manage).

Permission: settings.dashboard.manage

Request body: DashboardPrefsUpdate

Returns: 200 · DashboardPrefs

EndpointPermission
GET /settings/emailsettings.email.manage
PUT /settings/emailsettings.email.manage
DELETE /settings/emailsettings.email.manage
GET /settings/email/templatessettings.email.manage
PUT /settings/email/templatessettings.email.manage
POST /settings/email/templates/testsettings.email.manage
POST /settings/email/testsettings.email.manage
Parameters and schemas per endpoint

Get Email Settings — The org’s stored transport and the one actually sending (epic #199).

Permission: settings.email.manage

Returns: 200 · EmailSettingsRead

Save Email Settings

Permission: settings.email.manage

Request body: EmailSettingsWrite

Returns: 200 · EmailSettingsRead

Delete Email Settings

Permission: settings.email.manage

Returns: 204

List Email Templates

Permission: settings.email.manage

Returns: 200 · EmailTemplatesRead

Save Email Template — Upsert one (kind, locale) override; blank subject and body resets it to default.

Permission: settings.email.manage

Request body: EmailTemplateWrite

Returns: 200 · EmailTemplateItem

POST /api/v1/settings/email/templates/test

Section titled “POST /api/v1/settings/email/templates/test”

Test Email Template — Send a preview of the draft (or stored/default) to the acting admin.

Permission: settings.email.manage

Request body: EmailTemplateTest

Returns: 200 · EmailTestResult

Test Email Settings

Permission: settings.email.manage

Returns: 200 · EmailTestResult

EndpointPermission
GET /filesDeliberately open
POST /filesfiles.file.write
GET /files/{file_id}Deliberately open
PATCH /files/{file_id}files.file.write
DELETE /files/{file_id}files.file.write
GET /files/{file_id}/publicDeliberately open
GET /files/{file_id}/thumbnailDeliberately open
POST /files/inlinefiles.file.write
Parameters and schemas per endpoint

List Files — The files attached to one entity (a task’s documents, a project’s documents).

Permission: Deliberately open

Query parameters:

NameTypeMeaning
entity_typestring · required
entity_idstring · required
include_inlineboolean(default false)

Returns: 200 · StoredFileRead[]

Upload File — Multipart upload. Size and content type are bounded by instance config.

Permission: files.file.write

Query parameters:

NameTypeMeaning
entity_typestring
entity_idstring
inlineboolean(default false)

Request body: Body_upload_file_api_v1_files_post

Returns: 201 · StoredFileRead

Serve File — Stream the bytes. Cross-tenant ids read as 404 (tenant-scoped row lookup).

Permission: Deliberately open

Path parameters: file_id

Returns: 200

Update File — Tick or untick “the client may see this” on one attachment (the model’s client_visible). The only editable fact about a stored file: its bytes are immutable by construction (content-addressed) and its name is what the uploader gave it.

Permission: files.file.write

Path parameters: file_id

Request body: StoredFileUpdate

Returns: 200 · StoredFileRead

Delete File — Remove the row and its bytes. Branding/avatar files carry extra guards (service).

Permission: files.file.write

Path parameters: file_id

Returns: 204

Serve Public File — Anonymous serving for branding assets, org resolved strictly from the hostname.

Permission: Deliberately open

Path parameters: file_id

Query parameters:

NameTypeMeaning
sizeinteger
maskableboolean(default false)
bgstring(default #ffffff)

Returns: 200

Serve Thumbnail — A scaled-down preview of a stored raster image, so an attachment strip shows the screenshot rather than its filename and a client card shows the logo proof rather than a paperclip. Computed on demand, cached by ETag: the same 304 economy as the original.

Permission: Deliberately open

Path parameters: file_id

Query parameters:

NameTypeMeaning
sizeinteger(default 480)

Returns: 200

Upload File Inline — The same upload as POST /files, carried as base64 inside a JSON body.

Permission: files.file.write

Request body: InlineUpload

Returns: 201 · StoredFileRead

EndpointPermission
GET /google-analytics/propertiesgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}google_analytics.property.read
GET /google-analytics/properties/{property_id}/breakdowngoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/compatibilitygoogle_analytics.report.run
GET /google-analytics/properties/{property_id}/custom-dimensionsgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/custom-metricsgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/data-retentiongoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/data-streamsgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/firebase-linksgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/google-ads-linksgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/key-eventsgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/metadatagoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/overviewgoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/pivotgoogle_analytics.report.run
GET /google-analytics/properties/{property_id}/realtimegoogle_analytics.property.read
GET /google-analytics/properties/{property_id}/reportgoogle_analytics.report.run
GET /google-analytics/properties/{property_id}/timeseriesgoogle_analytics.property.read
Parameters and schemas per endpoint

List Google Analytics Properties — Every GA4 property the signed-in user’s Google account can read. Start here.

Permission: google_analytics.property.read

Query parameters:

NameTypeMeaning
querystringFilter by property name or id.

Returns: 200 · GoogleAnalyticsPropertyList

GET /api/v1/google-analytics/properties/{property_id}

Section titled “GET /api/v1/google-analytics/properties/{property_id}”

Get Google Analytics Property — One property’s own record: its currency, industry and reporting timezone.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsProperty

GET /api/v1/google-analytics/properties/{property_id}/breakdown

Section titled “GET /api/v1/google-analytics/properties/{property_id}/breakdown”

Google Analytics Breakdown — One dimension, ranked — top pages, channels, sources, devices, countries, events.

Permission: google_analytics.property.read

Path parameters: property_id

Query parameters:

NameTypeMeaning
dimensionstring · requiredOne GA4 dimension: pagePath, landingPage, sessionDefaultChannelGroup, sessionSource, sessionMedium, deviceCategory, country, city, eventName, browser, or any custom dimension from /metadata.
metricsstring[]Repeatable; defaults to sessions, totalUsers, keyEvents.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Dates are days in the property’s own reporting timezone — GET /properties/{property_id} tells you which.
limitinteger(default 25)
orderstringA named metric or dimension; prefix with - for descending.
filtersstring[]Dimension filters, repeatable: name==value (exact), name=@value (contains) or name=^value (begins with). All clauses must match. A clause that parses as none of these is refused rather than ignored.

Returns: 200 · GoogleAnalyticsReport

GET /api/v1/google-analytics/properties/{property_id}/compatibility

Section titled “GET /api/v1/google-analytics/properties/{property_id}/compatibility”

Google Analytics Compatibility — Which fields may still be combined with the ones named.

Permission: google_analytics.report.run

Path parameters: property_id

Query parameters:

NameTypeMeaning
dimensionsstring[]Already-chosen dimensions.
metricsstring[]Already-chosen metrics.

Returns: 200 · GoogleAnalyticsCompatibility

GET /api/v1/google-analytics/properties/{property_id}/custom-dimensions

Section titled “GET /api/v1/google-analytics/properties/{property_id}/custom-dimensions”

Google Analytics Custom Dimensions — The property’s own dimensions — the fields a report about this client can group by.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsResourceList

GET /api/v1/google-analytics/properties/{property_id}/custom-metrics

Section titled “GET /api/v1/google-analytics/properties/{property_id}/custom-metrics”

Google Analytics Custom Metrics — The property’s own metrics, with the unit each one is measured in.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsResourceList

GET /api/v1/google-analytics/properties/{property_id}/data-retention

Section titled “GET /api/v1/google-analytics/properties/{property_id}/data-retention”

Google Analytics Data Retention — How long this property keeps event-level data.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsResourceList

GET /api/v1/google-analytics/properties/{property_id}/data-streams

Section titled “GET /api/v1/google-analytics/properties/{property_id}/data-streams”

Google Analytics Data Streams — The web and app streams feeding this property, with their measurement IDs.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsResourceList

Section titled “GET /api/v1/google-analytics/properties/{property_id}/firebase-links”

Google Analytics Firebase Links — Which Firebase projects feed this property — the app half of a client’s measurement.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsResourceList

Section titled “GET /api/v1/google-analytics/properties/{property_id}/google-ads-links”

Google Analytics Google Ads Links — Which Google Ads customers this property is linked to.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsResourceList

GET /api/v1/google-analytics/properties/{property_id}/key-events

Section titled “GET /api/v1/google-analytics/properties/{property_id}/key-events”

Google Analytics Key Events — The events this property counts as conversions (GA4’s own name for them is key events).

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsResourceList

GET /api/v1/google-analytics/properties/{property_id}/metadata

Section titled “GET /api/v1/google-analytics/properties/{property_id}/metadata”

Google Analytics Metadata — Every dimension and metric this property will accept, custom ones included.

Permission: google_analytics.property.read

Path parameters: property_id

Returns: 200 · GoogleAnalyticsMetadata

GET /api/v1/google-analytics/properties/{property_id}/overview

Section titled “GET /api/v1/google-analytics/properties/{property_id}/overview”

Google Analytics Overview — How a property did over a period, against the same period a year earlier, with the change already computed — plus the acquisition channel split.

Permission: google_analytics.property.read

Path parameters: property_id

Query parameters:

NameTypeMeaning
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Dates are days in the property’s own reporting timezone — GET /properties/{property_id} tells you which.
comparestringyear (default — what seasonality survives) or previous.

Returns: 200 · GoogleAnalyticsOverview

GET /api/v1/google-analytics/properties/{property_id}/pivot

Section titled “GET /api/v1/google-analytics/properties/{property_id}/pivot”

Google Analytics Pivot — One dimension crossed against another: channel by month, device by landing page.

Permission: google_analytics.report.run

Path parameters: property_id

Query parameters:

NameTypeMeaning
metricsstring[] · requiredRepeatable GA4 metric names. At least one.
pivot_onstring · requiredThe dimension to spread across the columns.
dimensionsstring[]The dimension(s) that make the rows.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Dates are days in the property’s own reporting timezone — GET /properties/{property_id} tells you which.
limitinteger(default 25)

Returns: 200 · GoogleAnalyticsReport

GET /api/v1/google-analytics/properties/{property_id}/realtime

Section titled “GET /api/v1/google-analytics/properties/{property_id}/realtime”

Google Analytics Realtime — Who is on the site in the last thirty minutes. There is no period here, by design — realtime answers about now and about nothing else.

Permission: google_analytics.property.read

Path parameters: property_id

Query parameters:

NameTypeMeaning
dimensionsstring[]Repeatable realtime dimensions: unifiedScreenName, country, deviceCategory.
metricsstring[]Repeatable; defaults to activeUsers.
limitinteger(default 50)

Returns: 200 · GoogleAnalyticsRealtime

GET /api/v1/google-analytics/properties/{property_id}/report

Section titled “GET /api/v1/google-analytics/properties/{property_id}/report”

Google Analytics Report — Any dimensions crossed with any metrics — the escape hatch for questions the curated reads do not answer.

Permission: google_analytics.report.run

Path parameters: property_id

Query parameters:

NameTypeMeaning
metricsstring[] · requiredRepeatable GA4 metric names. At least one.
dimensionsstring[]Repeatable GA4 dimension names; none is a total.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Dates are days in the property’s own reporting timezone — GET /properties/{property_id} tells you which.
limitinteger(default 25)
offsetinteger(default 0)
orderstringA named metric or dimension; prefix with - for descending.
filtersstring[]Dimension filters, repeatable: name==value (exact), name=@value (contains) or name=^value (begins with). All clauses must match. A clause that parses as none of these is refused rather than ignored.

Returns: 200 · GoogleAnalyticsReport

GET /api/v1/google-analytics/properties/{property_id}/timeseries

Section titled “GET /api/v1/google-analytics/properties/{property_id}/timeseries”

Google Analytics Timeseries — The chosen metrics day by day, oldest first.

Permission: google_analytics.property.read

Path parameters: property_id

Query parameters:

NameTypeMeaning
metricsstring[]Repeatable GA4 metric names; defaults to sessions, totalUsers, keyEvents.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Dates are days in the property’s own reporting timezone — GET /properties/{property_id} tells you which.

Returns: 200 · GoogleAnalyticsReport

EndpointPermission
GET /google-search-console/ai-visibilitygoogle_search_console.site.read
GET /google-search-console/breakdowngoogle_search_console.site.read
GET /google-search-console/hourlygoogle_search_console.site.read
GET /google-search-console/inspectgoogle_search_console.site.read
GET /google-search-console/moversgoogle_search_console.site.read
GET /google-search-console/overviewgoogle_search_console.site.read
GET /google-search-console/querygoogle_search_console.report.run
GET /google-search-console/search-typesgoogle_search_console.site.read
GET /google-search-console/sitegoogle_search_console.site.read
GET /google-search-console/sitemapgoogle_search_console.site.read
GET /google-search-console/sitemapsgoogle_search_console.site.read
GET /google-search-console/sitesgoogle_search_console.site.read
GET /google-search-console/timeseriesgoogle_search_console.site.read
Parameters and schemas per endpoint

GET /api/v1/google-search-console/ai-visibility

Section titled “GET /api/v1/google-search-console/ai-visibility”

Google Search Console Ai Visibility — How visible the site is in Google’s generative AI features — AI Overviews and AI Mode — as far as the Search Console API can say.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Search Console keeps its days in Pacific time; the last two or three days are still being collected and are flagged as fresh.
comparestringyear (default — what seasonality survives) or previous.

Returns: 200 · GoogleSearchConsoleAiVisibility

GET /api/v1/google-search-console/breakdown

Section titled “GET /api/v1/google-search-console/breakdown”

Google Search Console Breakdown — One dimension, ranked — top queries, top pages, countries, devices, or the search appearances (rich results, video, product snippets…) the site was shown with.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
dimensionstringquery (default), page, country, device or searchAppearance. (default query)
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Search Console keeps its days in Pacific time; the last two or three days are still being collected and are flagged as fresh.
search_typestringweb (default; includes AI Overviews, which cannot be split out), image, video, news, discover or googleNews.
filtersstring[]Dimension filters, repeatable: dimension==value, dimension!=value, dimension=@value (contains), dimension!@value, dimension=~regex, dimension!~regex, on query, page, country (ISO 3166-1 alpha-3, lower case: nld), device (DESKTOP/MOBILE/TABLET) or searchAppearance. All clauses must match. A clause that parses as none of these is refused rather than ignored.
limitinteger(default 25)
offsetinteger(default 0)
orderstringGoogle ranks by clicks. Another metric (-impressions, -ctr, +position) is applied over the first thousand clicks-ranked rows, and the answer says so in warnings.

Returns: 200 · GoogleSearchConsoleReport

Google Search Console Hourly — Hour by hour over the last few days, today included and still moving — for “what happened this morning” and “did the launch land”. Every row may still change; there is no final hourly data, by Google’s design.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
daysintegerHow many days back, today included. Google keeps ten days of hourly rows. (default 2)
search_typestringweb (default; includes AI Overviews, which cannot be split out), image, video, news, discover or googleNews.

Returns: 200 · GoogleSearchConsoleReport

Google Search Console Inspect Url — What Google’s index holds for one URL: whether it is indexed (verdict PASS) and if not why (coverage_state, indexing_state, robots_txt_state, page_fetch_state), which canonical Google chose against the one the page declares, when it was last crawled, and the rich results found. One URL per call — Google allows 2 000 inspections a day per property.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
urlstring · requiredThe page to inspect. Must be under the property.
languagestringBCP-47 code for the issue texts, e.g. nl.

Returns: 200 · GoogleSearchConsoleInspection

Google Search Console Movers — Which queries or pages moved most in average position between this period and the one it is compared against. change is positive for a climb. entered and dropped count what appeared or vanished entirely, which the rows cannot show.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Search Console keeps its days in Pacific time; the last two or three days are still being collected and are flagged as fresh.
comparestringyear (default — what seasonality survives) or previous.
dimensionstringquery (default) or page. (default query)
limitinteger(default 25)
min_impressionsnumberIgnore anything shown fewer times than this in either span. (default 10)
search_typestringweb (default; includes AI Overviews, which cannot be split out), image, video, news, discover or googleNews.

Returns: 200 · GoogleSearchConsoleMovers

GET /api/v1/google-search-console/overview

Section titled “GET /api/v1/google-search-console/overview”

Google Search Console Overview — How a property did over a period — clicks, impressions, CTR and average position — against the same period a year earlier, with the change already computed, plus the split by device.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Search Console keeps its days in Pacific time; the last two or three days are still being collected and are flagged as fresh.
comparestringyear (default — what seasonality survives) or previous.
search_typestringweb (default; includes AI Overviews, which cannot be split out), image, video, news, discover or googleNews.

Returns: 200 · GoogleSearchConsoleOverview

Google Search Console Query — Any dimensions crossed, with any filters, any aggregation and any data state — the escape hatch for questions the curated reads do not answer (query by page, page by country, branded queries by device…).

Permission: google_search_console.report.run

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
dimensionsstring[]Repeatable, in row-key order: query, page, country, device, searchAppearance, date, hour. None is a total. hour forces data_state hourly_all.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Search Console keeps its days in Pacific time; the last two or three days are still being collected and are flagged as fresh.
search_typestringweb (default; includes AI Overviews, which cannot be split out), image, video, news, discover or googleNews.
filtersstring[]Dimension filters, repeatable: dimension==value, dimension!=value, dimension=@value (contains), dimension!@value, dimension=~regex, dimension!~regex, on query, page, country (ISO 3166-1 alpha-3, lower case: nld), device (DESKTOP/MOBILE/TABLET) or searchAppearance. All clauses must match. A clause that parses as none of these is refused rather than ignored.
aggregationstringauto (default), byProperty, byPage or byNewsShowcasePanel.
data_statestringall (default here: includes the fresh, still-changing days), final (Google’s default: finalised data only, so the last days are missing) or hourly_all.
limitinteger(default 25)
offsetinteger(default 0)

Returns: 200 · GoogleSearchConsoleReport

GET /api/v1/google-search-console/search-types

Section titled “GET /api/v1/google-search-console/search-types”

Google Search Console Search Types — The four metrics per Google surface — web, image, video, news, Discover, Google News — so “where is this site actually seen” is one answer. AI Overviews sit inside web and the API cannot split them out; see /ai-visibility for what it can say.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Search Console keeps its days in Pacific time; the last two or three days are still being collected and are flagged as fresh.

Returns: 200 · GoogleSearchConsoleSearchTypes

Get Google Search Console Site — One property’s record: its kind (domain or URL prefix) and what this account may do there.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.

Returns: 200 · GoogleSearchConsoleSite

Google Search Console Sitemap — One sitemap by its URL.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
feedpathstring · requiredThe sitemap’s own URL, as /sitemaps lists it.

Returns: 200 · GoogleSearchConsoleSitemap

GET /api/v1/google-search-console/sitemaps

Section titled “GET /api/v1/google-search-console/sitemaps”

Google Search Console Sitemaps — The sitemaps submitted for a property: when Google last read each, how many URLs it declares, and how many errors and warnings it carries. A sitemap nobody submitted, or one with errors, is the usual answer to “why are the new pages not indexed”.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.

Returns: 200 · GoogleSearchConsoleSitemapList

List Google Search Console Sites — Every Search Console property the signed-in user’s Google account can read. Start here.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
querystringFilter by site URL.

Returns: 200 · GoogleSearchConsoleSiteList

GET /api/v1/google-search-console/timeseries

Section titled “GET /api/v1/google-search-console/timeseries”

Google Search Console Timeseries — The four metrics day by day, oldest first. A day with nothing shown is omitted by Google rather than answered as zero.

Permission: google_search_console.site.read

Query parameters:

NameTypeMeaning
sitestring · requiredThe property exactly as GET /sites lists it: sc-domain:klant.nl for a domain property or https://www.klant.nl/ (trailing slash included) for a URL-prefix property. A bare hostname is read as the domain property.
periodstringThe span to report on: a trailing window (30d, 90d, 365d), a preset (month, last_month, quarter, last_quarter) or a named calendar period (2026-07, 2026-Q3). An unknown value falls back to 30d. Search Console keeps its days in Pacific time; the last two or three days are still being collected and are flagged as fresh.
search_typestringweb (default; includes AI Overviews, which cannot be split out), image, video, news, discover or googleNews.
filtersstring[]Dimension filters, repeatable: dimension==value, dimension!=value, dimension=@value (contains), dimension!@value, dimension=~regex, dimension!~regex, on query, page, country (ISO 3166-1 alpha-3, lower case: nld), device (DESKTOP/MOBILE/TABLET) or searchAppearance. All clauses must match. A clause that parses as none of these is refused rather than ignored.

Returns: 200 · GoogleSearchConsoleReport

EndpointPermission
GET /impex/company/columnscompanies.company.read
GET /impex/company/exportimpex.export, companies.company.read
POST /impex/company/importimpex.import, companies.company.write
POST /impex/company/inspectimpex.import, companies.company.write
GET /impex/contact/columnscontacts.contact.read
GET /impex/contact/exportimpex.export, contacts.contact.read
POST /impex/contact/importimpex.import, contacts.contact.write
POST /impex/contact/inspectimpex.import, contacts.contact.write
GET /impex/domain_tld_price/columnsdomains.tld_price.read
GET /impex/domain_tld_price/exportimpex.export, domains.tld_price.read
POST /impex/domain_tld_price/importimpex.import, domains.tld_price.manage
POST /impex/domain_tld_price/inspectimpex.import, domains.tld_price.manage
GET /impex/domain/columnsdomains.domain.read
GET /impex/domain/exportimpex.export, domains.domain.read
POST /impex/domain/importimpex.import, domains.domain.write
POST /impex/domain/inspectimpex.import, domains.domain.write
GET /impex/entitiesDeliberately open
GET /impex/hosting/columnshosting.hosting.read
GET /impex/hosting/exportimpex.export, hosting.hosting.read
POST /impex/hosting/importimpex.import, hosting.hosting.write
POST /impex/hosting/inspectimpex.import, hosting.hosting.write
GET /impex/invoice/columnsinvoicing.invoice.read
GET /impex/invoice/exportimpex.export, invoicing.invoice.read
POST /impex/invoice/importimpex.import, invoicing.invoice.write
POST /impex/invoice/inspectimpex.import, invoicing.invoice.write
GET /impex/project/columnsprojects.project.read
GET /impex/project/exportimpex.export, projects.project.read
POST /impex/project/importimpex.import, projects.project.write
POST /impex/project/inspectimpex.import, projects.project.write
GET /impex/subscription_template/columnssubscriptions.subscription.read
GET /impex/subscription_template/exportimpex.export, subscriptions.subscription.read
POST /impex/subscription_template/importimpex.import, subscriptions.template.manage
POST /impex/subscription_template/inspectimpex.import, subscriptions.template.manage
GET /impex/subscription_type/columnssubscriptions.subscription.read
GET /impex/subscription_type/exportimpex.export, subscriptions.subscription.read
POST /impex/subscription_type/importimpex.import, subscriptions.type.manage
POST /impex/subscription_type/inspectimpex.import, subscriptions.type.manage
GET /impex/subscription/columnssubscriptions.subscription.read
GET /impex/subscription/exportimpex.export, subscriptions.subscription.read
POST /impex/subscription/importimpex.import, subscriptions.subscription.write
POST /impex/subscription/inspectimpex.import, subscriptions.subscription.write
GET /impex/task/columnstasks.task.read
GET /impex/task/exportimpex.export, tasks.task.read
POST /impex/task/importimpex.import, tasks.task.write
POST /impex/task/inspectimpex.import, tasks.task.write
GET /impex/time_entry/columnstime.entry.read
GET /impex/time_entry/exportimpex.export, time.entry.read
POST /impex/time_entry/importimpex.import, time.entry.write
POST /impex/time_entry/inspectimpex.import, time.entry.write
GET /impex/uptime_monitor/columnsuptime.monitor.read
GET /impex/uptime_monitor/exportimpex.export, uptime.monitor.read
GET /impex/website/columnswebsites.website.read
GET /impex/website/exportimpex.export, websites.website.read
POST /impex/website/importimpex.import, websites.website.write
POST /impex/website/inspectimpex.import, websites.website.write
Parameters and schemas per endpoint

Impex Columns Company — Every column a company import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: companies.company.read

Returns: 200 · ImpexColumnsResponse

Impex Export Company — Export the current filtered company list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, companies.company.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
statusstringStatus, as on the list
minebooleanOnly rows assigned to me (default false)
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Company — Import company rows from a spreadsheet, upserting on the first of client_number, name each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, companies.company.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_company_api_v1_impex_company_import_post

Returns: 200 · ImportReport

Impex Inspect Company — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, companies.company.write

Request body: Body_impex_inspect_company_api_v1_impex_company_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Contact — Every column a contact import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: contacts.contact.read

Returns: 200 · ImpexColumnsResponse

Impex Export Contact — Export the current filtered contact list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, contacts.contact.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
company_idstring
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Contact — Import contact rows from a spreadsheet, upserting on the first of email each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, contacts.contact.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_contact_api_v1_impex_contact_import_post

Returns: 200 · ImportReport

Impex Inspect Contact — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, contacts.contact.write

Request body: Body_impex_inspect_contact_api_v1_impex_contact_inspect_post

Returns: 200 · ImpexInspectReport

GET /api/v1/impex/domain_tld_price/columns

Section titled “GET /api/v1/impex/domain_tld_price/columns”

Impex Columns Domain Tld Price — Every column a domain_tld_price import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: domains.tld_price.read

Returns: 200 · ImpexColumnsResponse

Impex Export Domain Tld Price — Export the current filtered domain_tld_price list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, domains.tld_price.read

Returns: 200

POST /api/v1/impex/domain_tld_price/import

Section titled “POST /api/v1/impex/domain_tld_price/import”

Impex Import Domain Tld Price — Import domain_tld_price rows from a spreadsheet, create-only (no natural key) (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, domains.tld_price.manage

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_domain_tld_price_api_v1_impex_domain_tld_price_import_post

Returns: 200 · ImportReport

POST /api/v1/impex/domain_tld_price/inspect

Section titled “POST /api/v1/impex/domain_tld_price/inspect”

Impex Inspect Domain Tld Price — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, domains.tld_price.manage

Request body: Body_impex_inspect_domain_tld_price_api_v1_impex_domain_tld_price_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Domain — Every column a domain import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: domains.domain.read

Returns: 200 · ImpexColumnsResponse

Impex Export Domain — Export the current filtered domain list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, domains.domain.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
company_idstring
statusstringStatus, as on the list
registrar_provider_idstring
dns_provider_idstring
invoiceableboolean
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Domain — Import domain rows from a spreadsheet, upserting on the first of name each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, domains.domain.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_domain_api_v1_impex_domain_import_post

Returns: 200 · ImportReport

Impex Inspect Domain — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, domains.domain.write

Request body: Body_impex_inspect_domain_api_v1_impex_domain_inspect_post

Returns: 200 · ImpexInspectReport

List Impex Entities — The entity types with CSV support, for the Instellingen → Import & export screen.

Permission: Deliberately open

Returns: 200 · ImpexEntityInfo[]

Impex Columns Hosting — Every column a hosting import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: hosting.hosting.read

Returns: 200 · ImpexColumnsResponse

Impex Export Hosting — Export the current filtered hosting list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, hosting.hosting.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
company_idstring
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Hosting — Import hosting rows from a spreadsheet, upserting on the first of name each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, hosting.hosting.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_hosting_api_v1_impex_hosting_import_post

Returns: 200 · ImportReport

Impex Inspect Hosting — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, hosting.hosting.write

Request body: Body_impex_inspect_hosting_api_v1_impex_hosting_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Invoice — Every column a invoice import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: invoicing.invoice.read

Returns: 200 · ImpexColumnsResponse

Impex Export Invoice — Export the current filtered invoice list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, invoicing.invoice.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
statusstringStatus, as on the list
company_idstring
overduebooleanOnly rows past their due date (default false)
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Invoice — Import invoice rows from a spreadsheet, upserting on the first of number each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, invoicing.invoice.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_invoice_api_v1_impex_invoice_import_post

Returns: 200 · ImportReport

Impex Inspect Invoice — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, invoicing.invoice.write

Request body: Body_impex_inspect_invoice_api_v1_impex_invoice_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Project — Every column a project import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: projects.project.read

Returns: 200 · ImpexColumnsResponse

Impex Export Project — Export the current filtered project list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, projects.project.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
statusstringStatus, as on the list
company_idstring
minebooleanOnly rows assigned to me (default false)
sortstringList sort key, ’-’ desc
burnstring

Returns: 200

Impex Import Project — Import project rows from a spreadsheet, upserting on the first of name each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, projects.project.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_project_api_v1_impex_project_import_post

Returns: 200 · ImportReport

Impex Inspect Project — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, projects.project.write

Request body: Body_impex_inspect_project_api_v1_impex_project_inspect_post

Returns: 200 · ImpexInspectReport

GET /api/v1/impex/subscription_template/columns

Section titled “GET /api/v1/impex/subscription_template/columns”

Impex Columns Subscription Template — Every column a subscription_template import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: subscriptions.subscription.read

Returns: 200 · ImpexColumnsResponse

GET /api/v1/impex/subscription_template/export

Section titled “GET /api/v1/impex/subscription_template/export”

Impex Export Subscription Template — Export the current filtered subscription_template list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, subscriptions.subscription.read

Returns: 200

POST /api/v1/impex/subscription_template/import

Section titled “POST /api/v1/impex/subscription_template/import”

Impex Import Subscription Template — Import subscription_template rows from a spreadsheet, upserting on the first of name each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, subscriptions.template.manage

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_subscription_template_api_v1_impex_subscription_template_import_post

Returns: 200 · ImportReport

POST /api/v1/impex/subscription_template/inspect

Section titled “POST /api/v1/impex/subscription_template/inspect”

Impex Inspect Subscription Template — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, subscriptions.template.manage

Request body: Body_impex_inspect_subscription_template_api_v1_impex_subscription_template_inspect_post

Returns: 200 · ImpexInspectReport

GET /api/v1/impex/subscription_type/columns

Section titled “GET /api/v1/impex/subscription_type/columns”

Impex Columns Subscription Type — Every column a subscription_type import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: subscriptions.subscription.read

Returns: 200 · ImpexColumnsResponse

GET /api/v1/impex/subscription_type/export

Section titled “GET /api/v1/impex/subscription_type/export”

Impex Export Subscription Type — Export the current filtered subscription_type list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, subscriptions.subscription.read

Returns: 200

POST /api/v1/impex/subscription_type/import

Section titled “POST /api/v1/impex/subscription_type/import”

Impex Import Subscription Type — Import subscription_type rows from a spreadsheet, upserting on the first of key each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, subscriptions.type.manage

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_subscription_type_api_v1_impex_subscription_type_import_post

Returns: 200 · ImportReport

POST /api/v1/impex/subscription_type/inspect

Section titled “POST /api/v1/impex/subscription_type/inspect”

Impex Inspect Subscription Type — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, subscriptions.type.manage

Request body: Body_impex_inspect_subscription_type_api_v1_impex_subscription_type_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Subscription — Every column a subscription import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: subscriptions.subscription.read

Returns: 200 · ImpexColumnsResponse

Impex Export Subscription — Export the current filtered subscription list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, subscriptions.subscription.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
statusstringStatus, as on the list
company_idstring
subscription_type_idstring
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Subscription — Import subscription rows from a spreadsheet, upserting on the first of name each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, subscriptions.subscription.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_subscription_api_v1_impex_subscription_import_post

Returns: 200 · ImportReport

Impex Inspect Subscription — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, subscriptions.subscription.write

Request body: Body_impex_inspect_subscription_api_v1_impex_subscription_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Task — Every column a task import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: tasks.task.read

Returns: 200 · ImpexColumnsResponse

Impex Export Task — Export the current filtered task list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, tasks.task.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
statusstringStatus, as on the list
company_idstring
project_idstring
minebooleanOnly rows assigned to me (default false)
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Task — Import task rows from a spreadsheet, create-only (no natural key) (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, tasks.task.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_task_api_v1_impex_task_import_post

Returns: 200 · ImportReport

Impex Inspect Task — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, tasks.task.write

Request body: Body_impex_inspect_task_api_v1_impex_task_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Time Entry — Every column a time_entry import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: time.entry.read

Returns: 200 · ImpexColumnsResponse

Impex Export Time Entry — Export the current filtered time_entry list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, time.entry.read

Query parameters:

NameTypeMeaning
minebooleanOnly rows assigned to me (default false)
user_idstring
company_idstring
project_idstring
task_idstring
date_fromstringRows on/after this day
date_tostringRows on/before this day
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Time Entry — Import time_entry rows from a spreadsheet, create-only (no natural key) (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, time.entry.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_time_entry_api_v1_impex_time_entry_import_post

Returns: 200 · ImportReport

Impex Inspect Time Entry — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, time.entry.write

Request body: Body_impex_inspect_time_entry_api_v1_impex_time_entry_inspect_post

Returns: 200 · ImpexInspectReport

Impex Columns Uptime Monitor — Every column a uptime_monitor import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: uptime.monitor.read

Returns: 200 · ImpexColumnsResponse

Impex Export Uptime Monitor — Export the current filtered uptime_monitor list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, uptime.monitor.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
company_idstring

Returns: 200

Impex Columns Website — Every column a website import can write into: the entity’s own, those contributed by other modules, and this organisation’s custom fields — with the labels, types and aliases a mapping UI needs.

Permission: websites.website.read

Returns: 200 · ImpexColumnsResponse

Impex Export Website — Export the current filtered website list as CSV (UTF-8, BOM). Headers are stable column keys plus the tenant’s custom-field keys — the file re-imports into the same organisation unchanged.

Permission: impex.export, websites.website.read

Query parameters:

NameTypeMeaning
qstringSearch, as on the list
company_idstring
hosting_idstring
uptime_enabledboolean
sortstringList sort key, ’-’ desc

Returns: 200

Impex Import Website — Import website rows from a spreadsheet, upserting on the first of domain each row fills (max 2000 data rows per request). Accepts a CSV/TSV/Excel upload or a pasted block; the format is read from the content, not the filename.

Permission: impex.import, websites.website.write

Query parameters:

NameTypeMeaning
dry_runbooleanValidate and report creates/updates/errors without writing anything. false applies the file all-or-nothing in one transaction. (default true)

Request body: Body_impex_import_website_api_v1_impex_website_import_post

Returns: 200 · ImportReport

Impex Inspect Website — Read an uploaded file and report what it is — format, worksheets, encoding, row count — plus each of its columns with sample cells and the suggested target column. Writes nothing and reads no records; returns a fingerprint the import repeats so a mapping cannot be applied to a different file.

Permission: impex.import, websites.website.write

Request body: Body_impex_inspect_website_api_v1_impex_website_inspect_post

Returns: 200 · ImpexInspectReport

EndpointPermission
GET /meetingsmeetings.meeting.read
POST /meetingsmeetings.meeting.write
GET /meetings/{meeting_id}meetings.meeting.read
PATCH /meetings/{meeting_id}meetings.meeting.write
DELETE /meetings/{meeting_id}meetings.meeting.delete
POST /meetings/{meeting_id}/ai/revisemeetings.meeting.write
POST /meetings/{meeting_id}/ai/transcribemeetings.meeting.write
DELETE /meetings/{meeting_id}/audiomeetings.meeting.write
POST /meetings/{meeting_id}/chunksmeetings.meeting.write
POST /meetings/{meeting_id}/confirmmeetings.meeting.write
POST /meetings/{meeting_id}/finishmeetings.meeting.write
PUT /meetings/{meeting_id}/minutesmeetings.meeting.write
PUT /meetings/{meeting_id}/participantsmeetings.meeting.write
GET /meetings/{meeting_id}/pdfmeetings.meeting.read
GET /meetings/{meeting_id}/previewmeetings.meeting.read
POST /meetings/{meeting_id}/redraftmeetings.meeting.write
POST /meetings/{meeting_id}/retrymeetings.meeting.write
GET /meetings/{meeting_id}/statusmeetings.meeting.read
GET /meetings/{meeting_id}/transcriptmeetings.meeting.read
GET /meetings/policymeetings.meeting.write
GET /meetings/settingsmeetings.settings.manage
PUT /meetings/settingsmeetings.settings.manage
GET /meetings/settings/designs/{design}/sourcemeetings.settings.manage
POST /meetings/settings/previewmeetings.settings.manage
GET /meetings/settings/sectionsmeetings.settings.manage
Parameters and schemas per endpoint

List Meetings

Permission: meetings.meeting.read

Query parameters:

NameTypeMeaning
company_idstring
project_idstring
statusstringComma-separated set; absent means every status
qstring
limitinteger(default 50)
offsetinteger(default 0)
countboolean(default true)

Returns: 200 · MeetingList

Create Meeting — Open a recording. Refused unless the caller states the participants were told.

Permission: meetings.meeting.write

Request body: MeetingCreate

Returns: 201 · MeetingDetail

Get Meeting

Permission: meetings.meeting.read

Path parameters: meeting_id

Returns: 200 · MeetingDetail

Update Meeting

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MeetingUpdate

Returns: 200 · MeetingDetail

Delete Meeting

Permission: meetings.meeting.delete

Path parameters: meeting_id

Returns: 204

POST /api/v1/meetings/{meeting_id}/ai/revise

Section titled “POST /api/v1/meetings/{meeting_id}/ai/revise”

Revise Meeting With Ai — One typed instruction, applied to the meeting as the caller: the title, client, project, kind and date, the roster and its speaker labels, and — while under review — every part of the minutes. The answer is a diff; what the instruction did not mention stays.

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MeetingReviseRequest

Returns: 200 · MeetingReviseResult

POST /api/v1/meetings/{meeting_id}/ai/transcribe

Section titled “POST /api/v1/meetings/{meeting_id}/ai/transcribe”

Transcribe Revise Instruction — Speech to text for the revise box: an instruction spoken instead of typed. Nothing is written; the words come back to be read first.

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MeetingTranscribeRequest

Returns: 200 · TimeTranscribeResult

DELETE /api/v1/meetings/{meeting_id}/audio

Section titled “DELETE /api/v1/meetings/{meeting_id}/audio”

Delete Audio — Drop the recording now rather than at the retention date; the words stay.

Permission: meetings.meeting.write

Path parameters: meeting_id

Returns: 200 · MeetingDetail

Add Chunk — One piece of the recording, base64 in JSON, while it is still being recorded.

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MeetingChunk

Returns: 200 · object

POST /api/v1/meetings/{meeting_id}/confirm

Section titled “POST /api/v1/meetings/{meeting_id}/confirm”

Confirm Meeting — The minutes become a contact moment and the ticked action items become tasks.

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MeetingConfirm

Returns: 200 · MeetingConfirmResult

Finish Meeting — The recorder stopped: the worker folds, transcribes and drafts.

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MeetingFinish

Returns: 200 · MeetingDetail

Save Minutes — The reviewer’s edits to the draft, kept without confirming.

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MinutesDraft

Returns: 200 · MeetingDetail

PUT /api/v1/meetings/{meeting_id}/participants

Section titled “PUT /api/v1/meetings/{meeting_id}/participants”

Set Participants — Who was in the meeting — a colleague, a contact of the client, or a name — and which speaker label (S1, S2 …) each of them is. Replaces the whole roster.

Permission: meetings.meeting.write

Path parameters: meeting_id

Request body: MeetingParticipants

Returns: 200 · MeetingDetail

Meeting Pdf — The minutes as a PDF, with the chapters the caller ticked (the transcript is off unless asked for). A read, so it survives an expired licence.

Permission: meetings.meeting.read

Path parameters: meeting_id

Query parameters:

NameTypeMeaning
sectionsstringComma-separated chapters; absent = the org’s defaults

Returns: 200

Preview Meeting — The minutes document as HTML — the same artefact the PDF prints.

Permission: meetings.meeting.read

Path parameters: meeting_id

Query parameters:

NameTypeMeaning
sectionsstringComma-separated chapters; absent = the org’s defaults

Returns: 200

POST /api/v1/meetings/{meeting_id}/redraft

Section titled “POST /api/v1/meetings/{meeting_id}/redraft”

Redraft Meeting — Draft the minutes again over the transcript already here — after naming the speakers, so the action items land on the people who took them on. No new transcription.

Permission: meetings.meeting.write

Path parameters: meeting_id

Returns: 200 · MeetingDetail

Retry Meeting

Permission: meetings.meeting.write

Path parameters: meeting_id

Returns: 200 · MeetingDetail

Meeting Status — The one column the detail page polls while a worker holds the row.

Permission: meetings.meeting.read

Path parameters: meeting_id

Returns: 200 · MeetingStatusRead

GET /api/v1/meetings/{meeting_id}/transcript

Section titled “GET /api/v1/meetings/{meeting_id}/transcript”

Meeting Transcript — The transcript, whole: as JSON (every line with its speaker’s name and the seconds it was said at — what an agent reads), or as a .txt, .md, .srt or .vtt file.

Permission: meetings.meeting.read

Path parameters: meeting_id

Query parameters:

NameTypeMeaning
formatstring(default json)

Returns: 200

Meeting Policy — What the recorder asks before it records: readable by whoever may record.

Permission: meetings.meeting.write

Returns: 200 · MeetingPolicy

Get Settings — The org’s meetings settings: the consent statement, the minutes document, the house writing instructions. No saved row means the defaults.

Permission: meetings.settings.manage

Returns: 200 · MeetingSettingsRead

Update Settings

Permission: meetings.settings.manage

Request body: MeetingSettingsUpdate

Returns: 200 · MeetingSettingsRead

GET /api/v1/meetings/settings/designs/{design}/source

Section titled “GET /api/v1/meetings/settings/designs/{design}/source”

Design Source — A shipped design’s own HTML and CSS, to start a custom minutes template from.

Permission: meetings.settings.manage

Path parameters: design

Returns: 200 · MeetingDesignSource

Preview Settings — Render an unsaved design over the org’s latest minuted meeting — the editor’s preview.

Permission: meetings.settings.manage

Request body: MeetingSettingsPreviewRequest

Returns: 200

Section Catalog — Every chapter a minutes document can carry, with the org’s default for each.

Permission: meetings.settings.manage

Returns: 200 · MeetingSectionCatalogEntry[]

EndpointPermission
GET /meta/domain-probeDeliberately open
GET /meta/instanceDeliberately open
GET /meta/mcpDeliberately open
GET /meta/meDeliberately open
PATCH /meta/meDeliberately open
GET /meta/modulesDeliberately open
GET /meta/tenantDeliberately open
PATCH /meta/tenantsettings.branding.write
GET /meta/tenant/domainsettings.domain.read
POST /meta/tenant/domainsettings.domain.write
DELETE /meta/tenant/domainsettings.domain.write
POST /meta/tenant/domain/checksettings.domain.write
GET /healthNone declared
GET /health/readyNone declared
Parameters and schemas per endpoint

Domain Probe — Answer, to whoever can reach this hostname, that this instance serves that org.

Permission: Deliberately open

Query parameters:

NameTypeMeaning
noncestring · required

Returns: 200 · DomainProbe

Deployment Meta

Permission: Deliberately open

Returns: 200 · InstanceMeta

Mcp Meta — The section catalog, read off the mounted server rather than rebuilt.

Permission: Deliberately open

Returns: 200 · McpMeta

Me

Permission: Deliberately open

Returns: 200 · MeInfo

Update Me — Update the current user’s own profile/preferences.

Permission: Deliberately open

Request body: MeUpdate

Returns: 200 · MeInfo

Modules

Permission: Deliberately open

Returns: 200 · ModulesMeta

Tenant Branding

Permission: Deliberately open

Returns: 200 · TenantBranding

Update Tenant Branding — Update the org’s white-label settings. Applied on next render.

Permission: settings.branding.write

Request body: TenantBrandingUpdate

Returns: 200 · TenantBranding

Domain Status

Permission: settings.domain.read

Returns: 200 · app__core__domainflow__DomainStatus

Claim Domain

Permission: settings.domain.write

Request body: DomainClaim

Returns: 200 · app__core__domainflow__DomainStatus

Clear Domain — Remove the custom domain (and any pending claim). The org keeps resolving via <slug>.<base_domain> — the UI warns that this changes the org’s address.

Permission: settings.domain.write

Query parameters:

NameTypeMeaning
pending_onlyboolean(default false)

Returns: 200 · app__core__domainflow__DomainStatus

Check Domain — Probe the current stage’s DNS/edge conditions, advance whatever they satisfy, and report each layer separately (ownership TXT, traffic DNS, hostname, certificate).

Permission: settings.domain.write

Returns: 200 · DomainCheckReport

Health — Liveness. Must stay cheap, dependency-free and unauthenticated: orchestrators poll it, and a probe that touches Postgres would restart a healthy API when the database blips. Readiness is a different question — see /health/ready.

Permission: None declared

Returns: 200 · object

Health Ready — Readiness: can this instance actually serve? Checks Postgres, Redis and Alembic.

Permission: None declared

Returns: 200

EndpointPermission
GET /microsoft/calendar/calendarsmicrosoft.connection.manage
PUT /microsoft/calendar/calendarsmicrosoft.connection.manage
GET /microsoft/calendar/channelsmicrosoft.calendar.read
GET /microsoft/calendar/eventsmicrosoft.calendar.read
POST /microsoft/calendar/webhookDeliberately open
GET /microsoft/connectionsmicrosoft.settings.manage
GET /microsoft/connections/memicrosoft.connection.manage
PATCH /microsoft/connections/memicrosoft.connection.manage
POST /microsoft/connections/me/disconnectmicrosoft.connection.manage
GET /microsoft/oauth/callbackmicrosoft.connection.manage
GET /microsoft/oauth/connectmicrosoft.connection.manage
GET /microsoft/onedrive/browsemicrosoft.onedrive.read
DELETE /microsoft/onedrive/files/{drive_id}/{item_id}microsoft.onedrive.write
PUT /microsoft/onedrive/foldermicrosoft.onedrive.write
POST /microsoft/onedrive/foldersmicrosoft.onedrive.write
GET /microsoft/onedrive/linksmicrosoft.onedrive.read
POST /microsoft/onedrive/linksmicrosoft.onedrive.write
DELETE /microsoft/onedrive/links/{link_id}microsoft.onedrive.write
POST /microsoft/onedrive/provisionmicrosoft.onedrive.write
POST /microsoft/onedrive/provision-allmicrosoft.settings.manage
GET /microsoft/onedrive/statemicrosoft.onedrive.read
POST /microsoft/onedrive/upload-sessionmicrosoft.onedrive.write
POST /microsoft/outlook/importinteractions.interaction.write
GET /microsoft/outlook/lookupmicrosoft.connection.manage
POST /microsoft/outlook/refreshmicrosoft.connection.manage
GET /microsoft/outlook/searchmicrosoft.connection.manage
GET /microsoft/outlook/statusmicrosoft.connection.manage
GET /microsoft/outlook/threads/{conversation_id}microsoft.connection.manage
GET /microsoft/settingsmicrosoft.settings.manage
PUT /microsoft/settingsmicrosoft.settings.manage
Parameters and schemas per endpoint

Microsoft Calendar List — The viewer’s own calendars — the selection UI’s read. Briefly cached; the existing Calendars.ReadWrite scope covers it, so no re-consent is needed.

Permission: microsoft.connection.manage

Returns: 200 · MicrosoftCalendarListEntry[]

Microsoft Set Calendar Selection — Choose which extra calendars sync for the viewer. Deselecting removes the calendar’s cached events on the spot; selecting queues a sync so the agenda fills without waiting.

Permission: microsoft.connection.manage

Request body: MicrosoftCalendarSelection

Returns: 200 · MicrosoftCalendarListEntry[]

Microsoft Calendar Channels — The viewer’s selection off the database alone — what the Agenda’s feeds menu reads on every open, so it never costs a Graph call.

Permission: microsoft.calendar.read

Returns: 200 · MicrosoftSelectedCalendar[]

Microsoft Calendar Events — The viewer’s own cached Outlook events. Reads the local cache, never Graph live.

Permission: microsoft.calendar.read

Query parameters:

NameTypeMeaning
date_fromstring · required
date_tostring · required

Returns: 200 · MicrosoftCalendarFeedItem[]

Microsoft Calendar Webhook

Permission: Deliberately open

Query parameters:

NameTypeMeaning
validationTokenstring

Returns: 200

List Connections

Permission: microsoft.settings.manage

Returns: 200 · MicrosoftConnectionRead[]

My Connection

Permission: microsoft.connection.manage

Returns: 200 · MyMicrosoftConnectionRead

Update My Connection

Permission: microsoft.connection.manage

Request body: MyMicrosoftConnectionUpdate

Returns: 200 · MyMicrosoftConnectionRead

POST /api/v1/microsoft/connections/me/disconnect

Section titled “POST /api/v1/microsoft/connections/me/disconnect”

Disconnect My Connection

Permission: microsoft.connection.manage

Returns: 204

Microsoft Oauth Callback — Store the grant and land the browser back where the connect started.

Permission: microsoft.connection.manage

Returns: 200

Oauth Connect — 302 to Microsoft’s consent screen, asking exactly the enabled surfaces’ scopes.

Permission: microsoft.connection.manage

Query parameters:

NameTypeMeaning
include_outlookboolean(default false)
nextstring(default “)

Returns: 200

Browse — Live folder contents as the viewing user — Graph’s permissions are authoritative.

Permission: microsoft.onedrive.read

Query parameters:

NameTypeMeaning
drive_idstring
folder_idstring
qstring
refreshboolean(default false)

Returns: 200 · OneDriveBrowseResult

DELETE /api/v1/microsoft/onedrive/files/{drive_id}/{item_id}

Section titled “DELETE /api/v1/microsoft/onedrive/files/{drive_id}/{item_id}”

Trash Item — Move an item to the drive’s recycle bin — the other half of unlink. Runs as the viewing user; drops every link naming the item, org-wide; refuses a non-empty folder.

Permission: microsoft.onedrive.write

Path parameters: drive_id, item_id

Returns: 204

Set Folder — Point a record at an existing folder — the picker’s target. The service adds microsoft.onedrive.manage when the record already has a folder (§15’s two layers).

Permission: microsoft.onedrive.write

Request body: OneDriveFolderSet

Returns: 200 · OneDriveLinkRead

Create Folder — Create a subfolder inside the folder being browsed, as the viewing user.

Permission: microsoft.onedrive.write

Request body: OneDriveFolderCreate

Returns: 201 · OneDriveFolder

List Links

Permission: microsoft.onedrive.read

Query parameters:

NameTypeMeaning
entity_typestring · required
entity_idstring · required
rollupboolean(default false)
limitinteger

Returns: 200 · OneDriveLinkRead[]

Create Link

Permission: microsoft.onedrive.write

Request body: OneDriveLinkCreate

Returns: 201 · OneDriveLinkRead

Section titled “DELETE /api/v1/microsoft/onedrive/links/{link_id}”

Delete Link — Unlink only. The item is never touched (the dialog says so too).

Permission: microsoft.onedrive.write

Path parameters: link_id

Returns: 204

Provision Entity — Queue one entity’s folder — the panel’s “create folder” button.

Permission: microsoft.onedrive.write

Request body: OneDriveProvisionRequest

Returns: 202

POST /api/v1/microsoft/onedrive/provision-all

Section titled “POST /api/v1/microsoft/onedrive/provision-all”

Provision All — Backfill: a folder for every client that has none (Instellingen → Microsoft 365).

Permission: microsoft.settings.manage

Returns: 200 · OneDriveBulkProvisionResult

Onedrive State

Permission: microsoft.onedrive.read

Query parameters:

NameTypeMeaning
entity_typestring
entity_idstring

Returns: 200 · OneDriveStateRead

POST /api/v1/microsoft/onedrive/upload-session

Section titled “POST /api/v1/microsoft/onedrive/upload-session”

Create Upload Session

Permission: microsoft.onedrive.write

Request body: OneDriveUploadSessionCreate

Returns: 200 · OneDriveUploadSession

Import Outlook Message — Log one message the poller skipped, filed where the caller says. The declared key is the one for the row this writes; reaching into the mailbox is asked for in the service.

Permission: interactions.interaction.write

Request body: OutlookImportRequest

Returns: 201 · OutlookImportResult

Lookup Outlook Message — Resolve a pasted reference to the conversation it names, in the caller’s own mailbox.

Permission: microsoft.connection.manage

Query parameters:

NameTypeMeaning
referencestring · requiredAn Outlook on the web link, a Graph message id, or an RFC-822 Message-ID

Returns: 200 · OutlookLookupResult

Refresh Outlook — Poll this mailbox once, now — rate-limited to one manual poll per minute.

Permission: microsoft.connection.manage

Returns: 200 · OutlookRefreshResult

Search Outlook — Find a message in the caller’s own mailbox, by who it was with and when — named fields, never raw KQL, so a colon in an address cannot become an operator.

Permission: microsoft.connection.manage

Query parameters:

NameTypeMeaning
participantstring
subjectstring
afterstring
beforestring

Returns: 200 · OutlookSearchResult

Read Outlook Status — When this mailbox was last polled, and whether asking for another one is worth it.

Permission: microsoft.connection.manage

Returns: 200 · OutlookSyncStatus

GET /api/v1/microsoft/outlook/threads/{conversation_id}

Section titled “GET /api/v1/microsoft/outlook/threads/{conversation_id}”

Read Outlook Thread — Every message of one conversation, marked with what is already on the timeline.

Permission: microsoft.connection.manage

Path parameters: conversation_id

Returns: 200 · OutlookLookupResult

Get Settings

Permission: microsoft.settings.manage

Returns: 200 · MicrosoftSettingsRead

Save Settings

Permission: microsoft.settings.manage

Request body: MicrosoftSettingsWrite

Returns: 200 · MicrosoftSettingsRead

EndpointPermission
GET /nav/prefsnav.prefs.read
PUT /nav/prefsnav.prefs.write
DELETE /nav/prefsnav.prefs.write
PUT /nav/prefs/defaultsettings.nav.manage
Parameters and schemas per endpoint

Get Prefs — The effective layout for the current user: own row → org default → none.

Permission: nav.prefs.read

Returns: 200 · NavPrefs

Set Prefs — A member’s personal order/visibility. Labels/groups are org config — stripped here.

Permission: nav.prefs.write

Request body: NavPrefsUpdate

Returns: 200 · NavPrefs

Reset Prefs — Drop the personal layout; the user falls back to the org default.

Permission: nav.prefs.write

Returns: 204

Set Default Prefs — The org-wide default that members inherit, plus tenant item/group labels (settings.nav.manage).

Permission: settings.nav.manage

Request body: NavPrefsUpdate

Returns: 200 · NavPrefs

EndpointPermission
GET /notificationsnotifications.notification.read
PATCH /notifications/{notification_id}notifications.notification.write
GET /notifications/activitynotifications.notification.read
GET /notifications/channelsnotifications.channels.manage_own
POST /notifications/channelsnotifications.channels.manage_own
PATCH /notifications/channels/{channel_id}notifications.channels.manage_own
DELETE /notifications/channels/{channel_id}notifications.channels.manage_own
POST /notifications/channels/{channel_id}/testnotifications.channels.manage_own
POST /notifications/mark-all-readnotifications.notification.write
GET /notifications/preferencesnotifications.notification.read
PUT /notifications/preferencesnotifications.notification.write
GET /notifications/preferences/defaultsnotifications.defaults.manage
PUT /notifications/preferences/defaultsnotifications.defaults.manage
GET /notifications/push/confignotifications.notification.read
GET /notifications/push/subscriptionsnotifications.notification.read
POST /notifications/push/subscriptionsnotifications.notification.write
DELETE /notifications/push/subscriptions/{subscription_id}notifications.notification.write
POST /notifications/push/testnotifications.notification.write
POST /notifications/push/unsubscribenotifications.notification.write
GET /notifications/unread-countnotifications.notification.read
GET /notifications/watchnotifications.notification.read
PUT /notifications/watchnotifications.notification.write
Parameters and schemas per endpoint

List Notifications

Permission: notifications.notification.read

Query parameters:

NameTypeMeaning
limitinteger(default 50)
offsetinteger(default 0)
unreadbooleantrue = unread only, false = read only
entity_typetask · project · company · leave_request · timesheet · interaction
sortstringcreated_at, ’-’ desc
countbooleanfalse skips the count query (docs/PERFORMANCE.md) (default true)

Returns: 200 · Page_NotificationRead_

PATCH /api/v1/notifications/{notification_id}

Section titled “PATCH /api/v1/notifications/{notification_id}”

Set Read — Reversible: read and unread are the same non-destructive toggle (docs/UX.md).

Permission: notifications.notification.write

Path parameters: notification_id

Request body: ReadUpdate

Returns: 200 · NotificationRead

Activity

Permission: notifications.notification.read

Query parameters:

NameTypeMeaning
entity_typetask · project · company · leave_request · timesheet · interaction · required
entity_idstring · required
limitinteger(default 20)

Returns: 200 · app__modules__notifications__schemas__ActivityItem[]

List Channels

Permission: notifications.channels.manage_own

Returns: 200 · ChannelRead[]

Create Channel

Permission: notifications.channels.manage_own

Request body: ChannelCreate

Returns: 201 · ChannelRead

PATCH /api/v1/notifications/channels/{channel_id}

Section titled “PATCH /api/v1/notifications/channels/{channel_id}”

Update Channel

Permission: notifications.channels.manage_own

Path parameters: channel_id

Request body: ChannelUpdate

Returns: 200 · ChannelRead

DELETE /api/v1/notifications/channels/{channel_id}

Section titled “DELETE /api/v1/notifications/channels/{channel_id}”

Delete Channel

Permission: notifications.channels.manage_own

Path parameters: channel_id

Returns: 204

POST /api/v1/notifications/channels/{channel_id}/test

Section titled “POST /api/v1/notifications/channels/{channel_id}/test”

Test Channel — Send a test message and report the provider’s real error (#17).

Permission: notifications.channels.manage_own

Path parameters: channel_id

Returns: 200 · ChannelTestResult

Mark All Read

Permission: notifications.notification.write

Returns: 200 · MarkAllResult

Get Preferences — My effective matrix: what will actually happen on both channels, and who decided it.

Permission: notifications.notification.read

Returns: 200 · PreferenceMatrix

Set Preferences

Permission: notifications.notification.write

Request body: PreferenceUpdate

Returns: 200 · PreferenceMatrix

GET /api/v1/notifications/preferences/defaults

Section titled “GET /api/v1/notifications/preferences/defaults”

Get Default Preferences — What a member inherits before they override anything (org-wide), plus the shared rooms.

Permission: notifications.defaults.manage

Returns: 200 · PreferenceMatrix

PUT /api/v1/notifications/preferences/defaults

Section titled “PUT /api/v1/notifications/preferences/defaults”

Set Default Preferences

Permission: notifications.defaults.manage

Request body: PreferenceUpdate

Returns: 200 · PreferenceMatrix

Push Config — The org’s VAPID public key, minting the keypair on first use.

Permission: notifications.notification.read

Returns: 200 · PushConfig

GET /api/v1/notifications/push/subscriptions

Section titled “GET /api/v1/notifications/push/subscriptions”

List Push Subscriptions — This person’s registered devices. The endpoint and key material never come back — the row exists to be recognised and revoked, and returning it would hand any XSS a push target.

Permission: notifications.notification.read

Query parameters:

NameTypeMeaning
endpointstringthe calling browser’s own endpoint, so its row can be marked current

Returns: 200 · PushSubscriptionRead[]

POST /api/v1/notifications/push/subscriptions

Section titled “POST /api/v1/notifications/push/subscriptions”

Register Push Subscription — Register (or refresh) the calling browser. Idempotent on the endpoint: the client re-presents it every session because endpoints rotate silently, and a rotated endpoint that nobody re-registered is a device that has stopped receiving without saying so.

Permission: notifications.notification.write

Request body: PushSubscriptionCreate

Returns: 201 · PushSubscriptionRead

DELETE /api/v1/notifications/push/subscriptions/{subscription_id}

Section titled “DELETE /api/v1/notifications/push/subscriptions/{subscription_id}”

Revoke Push Subscription

Permission: notifications.notification.write

Path parameters: subscription_id

Returns: 204

Test Push — Push a test notification to every device this person has registered.

Permission: notifications.notification.write

Returns: 200 · PushTestResult

POST /api/v1/notifications/push/unsubscribe

Section titled “POST /api/v1/notifications/push/unsubscribe”

Unsubscribe Push — Drop the device by the endpoint the browser itself holds — the only identifier it has after PushSubscription.unsubscribe(). Scoped to the caller, and silent on a miss: unsubscribing twice is not an error.

Permission: notifications.notification.write

Request body: PushUnsubscribe

Returns: 204

Unread Count — The bell’s badge. Comes from the API, never from counting the loaded page.

Permission: notifications.notification.read

Returns: 200 · UnreadCount

Get Watch

Permission: notifications.notification.read

Query parameters:

NameTypeMeaning
entity_typetask · project · company · leave_request · timesheet · interaction · required
entity_idstring · required

Returns: 200 · WatchRead

Set Watch

Permission: notifications.notification.write

Request body: WatchUpdate

Returns: 200 · WatchRead

EndpointPermission
GET /prefsDeliberately open
PUT /prefsDeliberately open
Parameters and schemas per endpoint

Get Prefs

Permission: Deliberately open

Returns: 200 · UserPrefs

Set Prefs

Permission: Deliberately open

Request body: UserPrefsUpdate

Returns: 200 · UserPrefs

EndpointPermission
GET /providerssettings.providers.read
POST /providerssettings.providers.manage
GET /providers/{provider_id}settings.providers.read
PATCH /providers/{provider_id}settings.providers.manage
DELETE /providers/{provider_id}settings.providers.manage
Parameters and schemas per endpoint

List Providers

Permission: settings.providers.read

Query parameters:

NameTypeMeaning
kindProviderKind
include_inactiveboolean(default false)

Returns: 200 · ProviderRead[]

Create Provider

Permission: settings.providers.manage

Request body: ProviderCreate

Returns: 201 · ProviderRead

Get Provider

Permission: settings.providers.read

Path parameters: provider_id

Returns: 200 · ProviderRead

Update Provider

Permission: settings.providers.manage

Path parameters: provider_id

Request body: ProviderUpdate

Returns: 200 · ProviderRead

Delete Provider

Permission: settings.providers.manage

Path parameters: provider_id

Returns: 204

EndpointPermission
GET /system/infosettings.system.read
Parameters and schemas per endpoint

System Info — Diagnostics for the Settings → System screen (settings.system.read).

Permission: settings.system.read

Returns: 200 · SystemInfo

EndpointPermission
GET /timeon/accountstimeon.settings.manage
POST /timeon/accountstimeon.settings.manage
PATCH /timeon/accounts/{account_id}timeon.settings.manage
DELETE /timeon/accounts/{account_id}timeon.settings.manage
POST /timeon/accounts/{account_id}/synctimeon.sync.run
POST /timeon/accounts/{account_id}/verifytimeon.settings.manage
GET /timeon/accounts/optionstimeon.sync.run
GET /timeon/conflictstimeon.sync.run
POST /timeon/conflicts/{conflict_id}/resolvetimeon.sync.write
GET /timeon/linkstimeon.sync.run
DELETE /timeon/links/{link_id}timeon.sync.write
GET /timeon/runstimeon.sync.run
GET /timeon/workspacetimeon.sync.run
Parameters and schemas per endpoint

List Accounts — Connected Timeon organisations. The API key is never part of the response.

Permission: timeon.settings.manage

Returns: 200 · TimeonAccountRead[]

Create Account — Store a credential. Both directions start at off: a connection that began syncing the moment a key was pasted would be an irreversible act performed by a form.

Permission: timeon.settings.manage

Request body: TimeonAccountCreate

Returns: 201 · TimeonAccountRead

PATCH /api/v1/timeon/accounts/{account_id}

Section titled “PATCH /api/v1/timeon/accounts/{account_id}”

Update Account — Rename, rotate the key, or change what the sync does. An omitted key keeps the stored one.

Permission: timeon.settings.manage

Path parameters: account_id

Request body: TimeonAccountUpdate

Returns: 200 · TimeonAccountRead

DELETE /api/v1/timeon/accounts/{account_id}

Section titled “DELETE /api/v1/timeon/accounts/{account_id}”

Delete Account — Forget the connection. Pairings, conflicts and runs go with it; time entries do not — a pulled entry is schakl’s record of work somebody did, and removing a credential is not a statement about whether that work happened.

Permission: timeon.settings.manage

Path parameters: account_id

Returns: 204

POST /api/v1/timeon/accounts/{account_id}/sync

Section titled “POST /api/v1/timeon/accounts/{account_id}/sync”

Run Sync — Run one sync and answer with its report.

Permission: timeon.sync.run

Path parameters: account_id

Request body: TimeonSyncRequest

Returns: 200 · TimeonSyncRunRead

POST /api/v1/timeon/accounts/{account_id}/verify

Section titled “POST /api/v1/timeon/accounts/{account_id}/verify”

Verify Account — Ask Timeon which organisation this key opens, and how big it is.

Permission: timeon.settings.manage

Path parameters: account_id

Returns: 200 · TimeonVerifyResult

Account Options — The connections a sync may run against.

Permission: timeon.sync.run

Returns: 200 · TimeonAccountRead[]

List Conflicts — The queue. Readable on sync.run — seeing what needs deciding must not require the power to decide it.

Permission: timeon.sync.run

Query parameters:

NameTypeMeaning
account_idstring
statusstring(default open)
limitinteger(default 50)
offsetinteger(default 0)

Returns: 200 · TimeonConflictRead[]

POST /api/v1/timeon/conflicts/{conflict_id}/resolve

Section titled “POST /api/v1/timeon/conflicts/{conflict_id}/resolve”

Resolve Conflict — Settle one conflict: keep schakl’s version, keep Timeon’s, or record that they may differ.

Permission: timeon.sync.write

Path parameters: conflict_id

Request body: TimeonConflictResolve

Returns: 200 · TimeonConflictRead

List Links — The pairings, filterable by what is wrong with them.

Permission: timeon.sync.run

Query parameters:

NameTypeMeaning
account_idstring
kindstring
statusstring
limitinteger(default 50)
offsetinteger(default 0)

Returns: 200 · TimeonLinkRead[]

Unpair — Forget one pairing without touching either record.

Permission: timeon.sync.write

Path parameters: link_id

Returns: 204

List Runs — What the last runs did, and what they could not do.

Permission: timeon.sync.run

Query parameters:

NameTypeMeaning
account_idstring
limitinteger(default 25)
offsetinteger(default 0)

Returns: 200 · TimeonSyncRunRead[]

Workspace — Everything the sync page’s shell draws, in one round trip.

Permission: timeon.sync.run

Query parameters:

NameTypeMeaning
account_idstring

Returns: 200 · TimeonWorkspaceRead

EndpointPermission
GET /trash/companycompanies.company.delete
GET /trash/company/{entity_id}companies.company.delete
DELETE /trash/company/{entity_id}companies.company.delete
GET /trash/company/{entity_id}/previewcompanies.company.delete
POST /trash/company/{entity_id}/restorecompanies.company.delete
Parameters and schemas per endpoint

Trash List Company — The company records in the trash: who deleted each, when, when it will be purged, and what hangs off it. A record with anything in blocking is kept past the retention window until somebody restores it.

Permission: companies.company.delete

Query parameters:

NameTypeMeaning
limitinteger(default 50)
offsetinteger(default 0)

Returns: 200 · TrashPage

Trash Get Company — One trashed company record. 404 for a live record: to this surface a row that is not in the trash does not exist.

Permission: companies.company.delete

Path parameters: entity_id

Returns: 200 · TrashItem

Trash Purge Company — Delete a trashed company record for good. Irreversible; refused (409) while anything in blocking still hangs off it. The nightly sweep does this by itself after the retention window.

Permission: companies.company.delete

Path parameters: entity_id

Returns: 204

GET /api/v1/trash/company/{entity_id}/preview

Section titled “GET /api/v1/trash/company/{entity_id}/preview”

Trash Preview Company — What deleting a live company record would do: blocking lists the records that stop it (issued invoices, domains, agreements, projects, hours — these outlive a client, so a client holding any is archived rather than deleted), taken_along what hides with it and goes when it is purged. Read this before calling DELETE.

Permission: companies.company.delete

Path parameters: entity_id

Returns: 200 · TrashPreview

POST /api/v1/trash/company/{entity_id}/restore

Section titled “POST /api/v1/trash/company/{entity_id}/restore”

Trash Restore Company — Bring a trashed company record back exactly as it was, with everything that hid with it.

Permission: companies.company.delete

Path parameters: entity_id

Returns: 204