Documentation Index
Fetch the complete documentation index at: https://docs.ivory.finance/llms.txt
Use this file to discover all available pages before exploring further.
Authentication
Authorization: Bearer <access_token>
All endpoints except the webhook require a JWT Bearer token. The tenant_id is extracted from the token’s sub claim.
Register a connector
POST /v1/admin/connectors
Registers a new connector and schedules its first run within 5 minutes. Validates that destination_catalog exists and is active for this tenant in lakehouse.tenant_catalogs.
Request body
| Field | Required | Default | Description |
|---|
source_system | Yes | — | Tap identity used for staging schema: salesforce, hubspot, stripe, etc. |
tap_name | Yes | — | Singer tap package name: tap-salesforce, tap-hubspot, tap-stripe, etc. |
destination_catalog | Yes | — | Must match a provisioned lakehouse.tenant_catalogs.catalog_name for this tenant |
destination_table | Yes | — | Final Iceberg table name (post-transform) |
sync_schedule | No | 0 2 * * * | Cron expression for the runner scheduler |
config_encrypted | No | null | Encrypted tap config (AES-256, encrypted client-side) |
tap_version | No | null | Pin a specific tap version; null = latest |
connector_type | No | meltano | meltano | airbyte | fivetran | custom |
meltano_environment | No | prod | Meltano environment name |
curl -X POST https://api.ivory.finance/v1/admin/connectors \
-H "Authorization: Bearer $IVORY_JWT" \
-H "Content-Type: application/json" \
-d '{
"source_system": "salesforce",
"tap_name": "tap-salesforce",
"destination_catalog": "ivory-lakehouse",
"destination_table": "salesforce_opportunities",
"sync_schedule": "0 */6 * * *"
}'
Response 201 Created
{
"id": "a1b2c3d4-...",
"source_system": "salesforce",
"tap_name": "tap-salesforce",
"iomete_namespace": "ivory_tenant_acme",
"iomete_table": "salesforce_opportunities",
"webhook_secret": "8f3a...",
"webhook_url": "/v1/etl/meltano/webhook",
"next_run_at": "~5 minutes",
"status": "active"
}
Save webhook_secret — it is only returned on creation and is never exposed again via the API. Configure it in your runner environment.
List connectors
Returns all connectors for the authenticated tenant with last-run summary.
curl https://api.ivory.finance/v1/admin/connectors \
-H "Authorization: Bearer $IVORY_JWT"
Response 200 OK
{
"connectors": [
{
"id": "a1b2c3d4-...",
"source_system": "salesforce",
"tap_name": "tap-salesforce",
"connector_type": "meltano",
"destination_catalog": "ivory-lakehouse",
"iomete_namespace": "ivory_tenant_acme",
"iomete_table": "salesforce_opportunities",
"sync_schedule": "0 */6 * * *",
"status": "active",
"consecutive_failures": 0,
"rows_total": 142830,
"last_synced_at": "2026-03-28T12:00:00Z",
"next_run_at": "2026-03-28T18:00:00Z",
"last_run_status": "completed",
"last_run_rows": 1204,
"last_run_rows_loaded": 1204,
"last_run_at": "2026-03-28T12:01:43Z",
"last_error": null
}
],
"count": 1
}
Get connector detail
GET /v1/admin/connectors/{id}
Full connector record including mapping count, run count, and cumulative IOMETE rows.
webhook_secret and config_encrypted are never returned.
Response 200 OK
{
"id": "a1b2c3d4-...",
"source_system": "salesforce",
"status": "active",
"mapping_count": 48,
"run_count": 312,
"total_rows_loaded_iomete": 142830,
"rows_total": 142830,
"tap_state": { "bookmarks": { "Opportunity": { "SystemModstamp": "2026-03-28T12:01:00Z" } } },
"next_run_at": "2026-03-28T18:00:00Z",
"consecutive_failures": 0,
"tap_version": null,
"meltano_environment": "prod"
}
Trigger a manual sync
POST /v1/admin/connectors/{id}/trigger
Sets next_run_at = now(). The runner picks it up within its next poll cycle (≤ 30 s).
Returns 409 if the connector is paused.
curl -X POST https://api.ivory.finance/v1/admin/connectors/a1b2c3d4-.../trigger \
-H "Authorization: Bearer $IVORY_JWT"
Response 202 Accepted
{
"connector_id": "a1b2c3d4-...",
"source_system": "salesforce",
"status": "queued",
"message": "Sync enqueued. The runner will pick it up within 60s."
}
Pause a connector
POST /v1/admin/connectors/{id}/pause
Stops the scheduler from dispatching new runs. Clears next_run_at. In-progress runs are not interrupted.
Response 200 OK
{ "connector_id": "a1b2c3d4-...", "status": "paused" }
Resume a connector
POST /v1/admin/connectors/{id}/resume
Re-activates a paused or errored connector. Sets next_run_at = now() + schedule_interval.
Response 200 OK
{ "connector_id": "a1b2c3d4-...", "status": "active" }
Delete a connector
DELETE /v1/admin/connectors/{id}
Permanently deletes the connector and cascade-deletes all field mappings, sync runs, and runner job records. Irreversible. The Singer STATE bookmark is also deleted — the next sync after re-registration will be a full load.
Response 204 No Content
Sync run history
GET /v1/admin/connectors/{id}/runs?limit=50
Returns sync run history, latest first.
| Query param | Default | Max | Description |
|---|
limit | 50 | 200 | Number of runs to return |
Response 200 OK
{
"runs": [
{
"id": "run-uuid-...",
"status": "completed",
"rows_synced": 1204,
"rows_loaded": 1204,
"transform_errors": 0,
"iomete_table": "ivory_tenant_acme.salesforce_opportunities",
"started_at": "2026-03-28T12:00:00Z",
"completed_at": "2026-03-28T12:01:43Z",
"duration_s": 103,
"error_msg": null
}
],
"count": 1
}
| Field | Description |
|---|
rows_synced | Rows read from the Singer tap (reported by the runner) |
rows_loaded | Rows successfully written to IOMETE Iceberg (after transform) |
transform_errors | Rows that failed the Python transform expression |
iomete_table | Fully-qualified Iceberg table written (namespace.table) |
Connector statuses
| Status | Description |
|---|
active | Scheduler is running; next run at next_run_at |
paused | Scheduler is disabled; no runs will be dispatched |
error | Circuit-breaker tripped after 3 consecutive failures; use resume to re-activate |
Webhook callback
POST /v1/etl/meltano/webhook
Called by meltano_runner_worker when a tap run completes. Protected by HMAC-SHA256 — not intended for direct client use. See Field Mappings for the full webhook flow.