{ "openapi": "3.1.0", "info": { "title": "Stripe Sync Service", "version": "1.0.0", "description": "Stripe Sync Service — manage pipelines and webhook ingress.\n\n## Endpoints\n\n| Method | Path | Summary |\n|--------|------|---------|\n| GET | /health | Health check |\n| GET | /pipelines | List pipelines |\n| POST | /pipelines | Create pipeline |\n| GET | /pipelines/{id} | Retrieve pipeline |\n| PATCH | /pipelines/{id} | Update pipeline |\n| DELETE | /pipelines/{id} | Delete pipeline |\n| POST | /pipelines/{id}/sync | Run sync for a pipeline |\n| POST | /pipelines/{id}/sync_batch | Run sync for a pipeline (batch, returns JSON) |\n| POST | /pipelines/{id}/check | Check pipeline connectivity |\n| POST | /pipelines/{id}/setup | Run pipeline setup hooks |\n| POST | /pipelines/{id}/teardown | Run pipeline teardown hooks |\n| POST | /pipelines/{id}/simulate_webhook_sync | Simulate webhook sync by fetching events from the Stripe API |\n| POST | /pipelines/{id}/sync_workflow_test | Run sync using the workflow backfill loop (no Temporal) |\n| POST | /webhooks/{pipeline_id} | Ingest a Stripe webhook event |" }, "paths": { "/health": { "get": { "operationId": "health", "tags": [ "Status" ], "summary": "Health check", "responses": { "200": { "description": "Server is healthy", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean", "const": true }, "hostname": { "type": "string" } }, "required": [ "ok", "hostname" ], "additionalProperties": false } } } } } } }, "/pipelines": { "get": { "operationId": "pipelines.list", "tags": [ "Pipelines" ], "summary": "List pipelines", "responses": { "200": { "description": "List of pipelines", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Pipeline" } }, "has_more": { "type": "boolean" } }, "required": [ "data", "has_more" ], "additionalProperties": false } } } } } }, "post": { "operationId": "pipelines.create", "tags": [ "Pipelines" ], "summary": "Create pipeline", "parameters": [ { "in": "query", "name": "skip_check", "schema": { "description": "Skip connector validation checks", "type": "boolean" }, "description": "Skip connector validation checks" } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "description": "Optional pipeline identifier. If omitted, the service generates one (e.g. pipe_abc123).", "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$" }, "source": { "$ref": "#/components/schemas/SourceConfig" }, "destination": { "$ref": "#/components/schemas/DestinationConfig" }, "streams": { "description": "Selected streams to sync. All streams synced if omitted.", "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "Stream (table) name to sync." }, "sync_mode": { "description": "How the source reads this stream. Defaults to full_refresh.", "type": "string", "enum": [ "incremental", "full_refresh" ] }, "backfill_limit": { "description": "Cap backfill to this many records, then mark the stream complete.", "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 } }, "required": [ "name" ] } } }, "required": [ "source", "destination" ] } } } }, "responses": { "201": { "description": "Created pipeline", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pipeline" } } } }, "400": { "description": "Invalid input", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } }, "409": { "description": "Pipeline id already exists", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}": { "get": { "operationId": "pipelines.get", "tags": [ "Pipelines" ], "summary": "Retrieve pipeline", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." } ], "responses": { "200": { "description": "Retrieved pipeline with status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pipeline" } } } }, "404": { "description": "Not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } }, "patch": { "operationId": "pipelines.update", "tags": [ "Pipelines" ], "summary": "Update pipeline", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "description": "Optional pipeline identifier. If omitted, the service generates one (e.g. pipe_abc123).", "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$" }, "source": { "$ref": "#/components/schemas/SourceConfig" }, "destination": { "$ref": "#/components/schemas/DestinationConfig" }, "streams": { "description": "Selected streams to sync. All streams synced if omitted.", "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "Stream (table) name to sync." }, "sync_mode": { "description": "How the source reads this stream. Defaults to full_refresh.", "type": "string", "enum": [ "incremental", "full_refresh" ] }, "backfill_limit": { "description": "Cap backfill to this many records, then mark the stream complete.", "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 } }, "required": [ "name" ] } }, "desired_status": { "description": "Set to \"paused\" to pause, \"active\" to resume, \"deleted\" to tear down.", "type": "string", "enum": [ "active", "paused", "deleted" ] } } } } } }, "responses": { "200": { "description": "Updated pipeline", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pipeline" } } } }, "400": { "description": "Bad request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } }, "404": { "description": "Not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } }, "409": { "description": "Invalid status transition", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } }, "delete": { "operationId": "pipelines.delete", "tags": [ "Pipelines" ], "summary": "Delete pipeline", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." } ], "responses": { "200": { "description": "Deleted pipeline", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "deleted": { "type": "boolean", "const": true } }, "required": [ "id", "deleted" ], "additionalProperties": false } } } }, "404": { "description": "Not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}/sync": { "post": { "operationId": "pipelines.sync", "tags": [ "Pipelines" ], "summary": "Run sync for a pipeline", "description": "Triggers an ad-hoc sync run for the pipeline and streams NDJSON messages (records, state, progress, eof) back to the client. Persists the ending sync_state on the pipeline so the next run resumes where this one left off.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, { "in": "query", "name": "time_limit", "schema": { "description": "Stop after N seconds", "type": "number" }, "description": "Stop after N seconds" }, { "in": "query", "name": "run_id", "schema": { "description": "Sync run identifier (resumes or starts fresh)", "type": "string" }, "description": "Sync run identifier (resumes or starts fresh)" }, { "in": "query", "name": "reset_state", "schema": { "description": "Ignore persisted sync state and start fresh (ending state is still saved)", "type": "boolean" }, "description": "Ignore persisted sync state and start fresh (ending state is still saved)" } ], "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "properties": { "source": { "$ref": "#/components/schemas/SourceConfig" }, "destination": { "$ref": "#/components/schemas/DestinationConfig" }, "streams": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "Stream (table) name to sync." }, "sync_mode": { "description": "How the source reads this stream. Defaults to full_refresh.", "type": "string", "enum": [ "incremental", "full_refresh" ] }, "backfill_limit": { "description": "Cap backfill to this many records, then mark the stream complete.", "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 } }, "required": [ "name" ] } }, "sync_state": { "description": "Explicit sync checkpoint override for resumed ad-hoc runs", "$ref": "#/components/schemas/SyncState" } } } } } }, "responses": { "200": { "description": "Streaming NDJSON sync output", "content": { "application/x-ndjson": { "schema": { "type": "object", "properties": {}, "additionalProperties": {} } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}/sync_batch": { "post": { "operationId": "pipelines.sync_batch", "tags": [ "Pipelines" ], "summary": "Run sync for a pipeline (batch, returns JSON)", "description": "Runs the full sync pipeline and returns the final EofPayload as a single JSON response. Persists the ending sync_state on the pipeline so the next run resumes where this one left off.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, { "in": "query", "name": "state_limit", "schema": { "description": "Stop after N source_state messages", "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "description": "Stop after N source_state messages" }, { "in": "query", "name": "run_id", "schema": { "description": "Sync run identifier (resumes or starts fresh)", "type": "string" }, "description": "Sync run identifier (resumes or starts fresh)" }, { "in": "query", "name": "reset_state", "schema": { "description": "Ignore persisted sync state and start fresh (ending state is still saved)", "type": "boolean" }, "description": "Ignore persisted sync state and start fresh (ending state is still saved)" } ], "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "properties": { "source": { "$ref": "#/components/schemas/SourceConfig" }, "destination": { "$ref": "#/components/schemas/DestinationConfig" }, "streams": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "Stream (table) name to sync." }, "sync_mode": { "description": "How the source reads this stream. Defaults to full_refresh.", "type": "string", "enum": [ "incremental", "full_refresh" ] }, "backfill_limit": { "description": "Cap backfill to this many records, then mark the stream complete.", "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 } }, "required": [ "name" ] } }, "sync_state": { "description": "Explicit sync checkpoint override for resumed ad-hoc runs", "$ref": "#/components/schemas/SyncState" } } } } } }, "responses": { "200": { "description": "Sync result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EofPayload" } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}/check": { "post": { "operationId": "pipelines.check", "tags": [ "Pipelines" ], "summary": "Check pipeline connectivity", "description": "Runs the `check()` method on source and/or destination connectors and streams back NDJSON messages (connection_status, log, trace). Pass ?only=source or ?only=destination to check a single side.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, { "in": "query", "name": "only", "schema": { "description": "Run only the source or destination side", "example": "destination", "type": "string", "enum": [ "source", "destination" ] }, "description": "Run only the source or destination side" } ], "responses": { "200": { "description": "Streaming NDJSON check output", "content": { "application/x-ndjson": { "schema": { "type": "object", "properties": {}, "additionalProperties": {} } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}/setup": { "post": { "operationId": "pipelines.setup", "tags": [ "Pipelines" ], "summary": "Run pipeline setup hooks", "description": "Runs the `setup()` method on source and/or destination connectors (e.g. creating destination tables). Streams NDJSON messages (control, log, trace). Pass ?only=source or ?only=destination to run a single side.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, { "in": "query", "name": "only", "schema": { "description": "Run only the source or destination side", "example": "destination", "type": "string", "enum": [ "source", "destination" ] }, "description": "Run only the source or destination side" } ], "responses": { "200": { "description": "Streaming NDJSON setup output", "content": { "application/x-ndjson": { "schema": { "type": "object", "properties": {}, "additionalProperties": {} } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}/teardown": { "post": { "operationId": "pipelines.teardown", "tags": [ "Pipelines" ], "summary": "Run pipeline teardown hooks", "description": "Runs the `teardown()` method on source and/or destination connectors (e.g. dropping destination tables). Streams NDJSON messages (log, trace). Pass ?only=source or ?only=destination to run a single side.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, { "in": "query", "name": "only", "schema": { "description": "Run only the source or destination side", "example": "destination", "type": "string", "enum": [ "source", "destination" ] }, "description": "Run only the source or destination side" } ], "responses": { "200": { "description": "Streaming NDJSON teardown output", "content": { "application/x-ndjson": { "schema": { "type": "object", "properties": {}, "additionalProperties": {} } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}/simulate_webhook_sync": { "post": { "operationId": "pipelines.simulate_webhook_sync", "tags": [ "Pipelines" ], "summary": "Simulate webhook sync by fetching events from the Stripe API", "description": "Fetches events from /v1/events using the pipeline's Stripe API key, then pipes them as input into the sync engine's push mode. This exercises the same code path as real webhooks without needing webhook delivery.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, { "in": "query", "name": "created_after", "schema": { "description": "Only fetch events created after this Unix timestamp (default: 24 hours ago)", "type": "number" }, "description": "Only fetch events created after this Unix timestamp (default: 24 hours ago)" }, { "in": "query", "name": "limit", "schema": { "description": "Max events to fetch (default: all)", "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "description": "Max events to fetch (default: all)" } ], "responses": { "200": { "description": "Streaming NDJSON sync output", "content": { "application/x-ndjson": { "schema": { "type": "object", "properties": {}, "additionalProperties": {} } } } }, "400": { "description": "Pipeline source is not Stripe", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/pipelines/{id}/sync_workflow_test": { "post": { "operationId": "pipelines.sync_workflow_test", "tags": [ "Pipelines" ], "summary": "Run sync using the workflow backfill loop (no Temporal)", "description": "Exercises the same backfill loop code that the Temporal workflow uses, but runs inline without a Temporal server. Useful for testing the full workflow logic end-to-end.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123).", "example": "pipe_abc123" }, "required": true, "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, { "in": "query", "name": "time_limit", "schema": { "description": "Time limit per iteration (seconds)", "type": "number" }, "description": "Time limit per iteration (seconds)" } ], "responses": { "200": { "description": "Backfill result with final eof and sync state", "content": { "application/json": { "schema": { "type": "object", "properties": { "eof": { "type": "object", "properties": {}, "additionalProperties": {} }, "sync_state": { "type": "object", "properties": {}, "additionalProperties": {} } }, "required": [ "eof" ], "additionalProperties": false } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": {} }, "required": [ "error" ], "additionalProperties": false } } } } } } }, "/webhooks/{pipeline_id}": { "post": { "operationId": "webhooks.push", "tags": [ "Webhooks" ], "summary": "Ingest a Stripe webhook event", "description": "Receives a raw Stripe webhook event, verifies its signature using the pipeline's webhook secret, and enqueues it for processing by the active pipeline.", "parameters": [ { "in": "path", "name": "pipeline_id", "schema": { "type": "string", "example": "pipe_abc123" }, "required": true } ], "responses": { "200": { "description": "Event accepted", "content": { "text/plain": { "schema": { "type": "string", "const": "ok" } } } } } } } }, "components": { "schemas": { "SourceConfig": { "oneOf": [ { "type": "object", "properties": { "type": { "type": "string", "const": "stripe" }, "stripe": { "$ref": "#/components/schemas/SourceStripeConfig" } }, "required": [ "type", "stripe" ] }, { "type": "object", "properties": { "type": { "type": "string", "const": "postgres" }, "postgres": { "$ref": "#/components/schemas/SourcePostgresConfig" } }, "required": [ "type", "postgres" ] }, { "type": "object", "properties": { "type": { "type": "string", "const": "metronome" }, "metronome": { "$ref": "#/components/schemas/SourceMetronomeConfig" } }, "required": [ "type", "metronome" ] } ], "type": "object", "discriminator": { "propertyName": "type" } }, "SourceStripeConfig": { "type": "object", "properties": { "api_key": { "type": "string", "description": "Stripe API key (sk_test_... or sk_live_...)" }, "account_id": { "type": "string", "description": "Stripe account ID (resolved from API if omitted)" }, "account_created": { "type": "integer", "minimum": 0, "maximum": 9007199254740991, "description": "Stripe account creation timestamp in unix seconds (resolved from API if omitted)" }, "livemode": { "type": "boolean", "description": "Whether this is a live mode sync" }, "api_version": { "type": "string", "enum": [ "2026-03-25.dahlia", "2026-02-25.clover", "2026-01-28.clover", "2025-12-15.clover", "2025-11-17.clover", "2025-10-29.clover", "2025-09-30.clover", "2025-08-27.basil", "2025-07-30.basil", "2025-06-30.basil", "2025-05-28.basil", "2025-04-30.basil", "2025-03-31.basil", "2025-02-24.acacia", "2025-01-27.acacia", "2024-12-18.acacia", "2024-11-20.acacia", "2024-10-28.acacia", "2024-09-30.acacia", "2024-06-20", "2024-04-10", "2024-04-03", "2023-10-16", "2023-08-16", "2022-11-15", "2022-08-01", "2020-08-27", "2020-03-02", "2019-12-03", "2019-11-05", "2019-10-17", "2019-10-08", "2019-09-09", "2019-08-14", "2019-05-16", "2019-03-14", "2019-02-19", "2019-02-11", "2018-11-08", "2018-10-31", "2018-09-24", "2018-09-06", "2018-08-23", "2018-07-27", "2018-05-21", "2018-02-28", "2018-02-06", "2018-02-05", "2018-01-23", "2017-12-14", "2017-08-15" ] }, "base_url": { "type": "string", "format": "uri", "description": "Override the Stripe API base URL (e.g. http://localhost:12111 for stripe-mock)" }, "webhook_url": { "type": "string", "format": "uri", "description": "URL for managed webhook endpoint registration" }, "webhook_secret": { "type": "string", "description": "Webhook signing secret (whsec_...) for signature verification" }, "websocket": { "type": "boolean", "description": "Enable WebSocket streaming for live events" }, "poll_events": { "type": "boolean", "description": "Enable events API polling for incremental sync after backfill" }, "webhook_port": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Port for built-in webhook HTTP listener (e.g. 4242)" }, "revalidate_objects": { "type": "array", "items": { "type": "string" }, "description": "Object types to re-fetch from Stripe API on webhook (e.g. [\"subscription\"])" }, "backfill_limit": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Max objects to backfill per stream (useful for testing)" }, "rate_limit": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Override max requests per second (default: auto-derived from API key mode — 20 live, 10 test)." } }, "required": [ "api_key" ], "additionalProperties": false }, "SourcePostgresConfig": { "allOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "anyOf": [ { "type": "object", "properties": { "schema": { "default": "public", "type": "string", "description": "Schema containing the source table" }, "primary_key": { "default": [ "id" ], "minItems": 1, "type": "array", "items": { "type": "string" }, "description": "Columns that uniquely identify a row in this stream" }, "cursor_field": { "type": "string", "description": "Monotonic column used for incremental reads" }, "page_size": { "default": 100, "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Rows to read per page" }, "ssl_ca_pem": { "type": "string", "description": "PEM-encoded CA certificate for SSL verification (required for verify-ca / verify-full with a private CA)" }, "url": { "type": "string", "description": "Postgres connection string" }, "connection_string": { "type": "string", "description": "Deprecated alias for url; prefer url" }, "table": { "type": "string", "description": "Table to read from" }, "query": { "not": {} }, "stream": { "type": "string", "description": "Stream name emitted in the catalog and records. Defaults to table name." } }, "required": [ "cursor_field", "url", "table" ], "additionalProperties": false }, { "type": "object", "properties": { "schema": { "default": "public", "type": "string", "description": "Schema containing the source table" }, "primary_key": { "default": [ "id" ], "minItems": 1, "type": "array", "items": { "type": "string" }, "description": "Columns that uniquely identify a row in this stream" }, "cursor_field": { "type": "string", "description": "Monotonic column used for incremental reads" }, "page_size": { "default": 100, "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Rows to read per page" }, "ssl_ca_pem": { "type": "string", "description": "PEM-encoded CA certificate for SSL verification (required for verify-ca / verify-full with a private CA)" }, "url": { "type": "string", "description": "Postgres connection string" }, "connection_string": { "type": "string", "description": "Deprecated alias for url; prefer url" }, "table": { "type": "string", "description": "Table to read from" }, "query": { "not": {} }, "stream": { "type": "string", "description": "Stream name emitted in the catalog and records. Defaults to table name." } }, "required": [ "cursor_field", "connection_string", "table" ], "additionalProperties": false }, { "type": "object", "properties": { "schema": { "default": "public", "type": "string", "description": "Schema containing the source table" }, "primary_key": { "default": [ "id" ], "minItems": 1, "type": "array", "items": { "type": "string" }, "description": "Columns that uniquely identify a row in this stream" }, "cursor_field": { "type": "string", "description": "Monotonic column used for incremental reads" }, "page_size": { "default": 100, "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Rows to read per page" }, "ssl_ca_pem": { "type": "string", "description": "PEM-encoded CA certificate for SSL verification (required for verify-ca / verify-full with a private CA)" }, "url": { "type": "string", "description": "Postgres connection string" }, "connection_string": { "type": "string", "description": "Deprecated alias for url; prefer url" }, "table": { "not": {} }, "query": { "type": "string", "description": "SQL query to read from. Must expose the primary_key and cursor_field columns." }, "stream": { "type": "string", "description": "Stream name emitted in the catalog and records." } }, "required": [ "cursor_field", "url", "query", "stream" ], "additionalProperties": false }, { "type": "object", "properties": { "schema": { "default": "public", "type": "string", "description": "Schema containing the source table" }, "primary_key": { "default": [ "id" ], "minItems": 1, "type": "array", "items": { "type": "string" }, "description": "Columns that uniquely identify a row in this stream" }, "cursor_field": { "type": "string", "description": "Monotonic column used for incremental reads" }, "page_size": { "default": 100, "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Rows to read per page" }, "ssl_ca_pem": { "type": "string", "description": "PEM-encoded CA certificate for SSL verification (required for verify-ca / verify-full with a private CA)" }, "url": { "type": "string", "description": "Postgres connection string" }, "connection_string": { "type": "string", "description": "Deprecated alias for url; prefer url" }, "table": { "not": {} }, "query": { "type": "string", "description": "SQL query to read from. Must expose the primary_key and cursor_field columns." }, "stream": { "type": "string", "description": "Stream name emitted in the catalog and records." } }, "required": [ "cursor_field", "connection_string", "query", "stream" ], "additionalProperties": false } ] } ] }, "SourceMetronomeConfig": { "type": "object", "properties": { "api_key": { "type": "string", "description": "Metronome API bearer token" }, "base_url": { "type": "string", "format": "uri", "description": "Override the Metronome API base URL (default: https://api.metronome.com)" }, "rate_limit": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Max requests per second (default: no limit)" }, "backfill_limit": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991, "description": "Max records to fetch per stream (useful for testing)" }, "webhook_secret": { "type": "string", "description": "Webhook signing secret for HMAC-SHA256 signature verification" }, "webhook_port": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Port for built-in webhook HTTP listener (e.g. 4243)" } }, "required": [ "api_key" ], "additionalProperties": false }, "DestinationConfig": { "oneOf": [ { "type": "object", "properties": { "type": { "type": "string", "const": "postgres" }, "postgres": { "$ref": "#/components/schemas/DestinationPostgresConfig" } }, "required": [ "type", "postgres" ] }, { "type": "object", "properties": { "type": { "type": "string", "const": "google_sheets" }, "google_sheets": { "$ref": "#/components/schemas/DestinationGoogleSheetsConfig" } }, "required": [ "type", "google_sheets" ] }, { "type": "object", "properties": { "type": { "type": "string", "const": "stripe" }, "stripe": { "$ref": "#/components/schemas/DestinationStripeConfig" } }, "required": [ "type", "stripe" ] }, { "type": "object", "properties": { "type": { "type": "string", "const": "redis" }, "redis": { "$ref": "#/components/schemas/DestinationRedisConfig" } }, "required": [ "type", "redis" ] } ], "type": "object", "discriminator": { "propertyName": "type" } }, "DestinationPostgresConfig": { "type": "object", "properties": { "url": { "type": "string", "description": "Postgres connection string" }, "connection_string": { "type": "string", "description": "Deprecated alias for url; prefer url" }, "schema": { "default": "public", "type": "string", "description": "Target schema name (e.g. \"stripe\")" }, "batch_size": { "default": 100, "type": "number", "description": "Records to buffer before flushing" }, "aws": { "type": "object", "properties": { "host": { "type": "string", "description": "Postgres host for RDS IAM auth" }, "port": { "default": 5432, "type": "number", "description": "Postgres port for RDS IAM auth" }, "database": { "type": "string", "description": "Database name for RDS IAM auth" }, "user": { "type": "string", "description": "Database user for RDS IAM auth" }, "region": { "type": "string", "description": "AWS region for RDS instance" }, "role_arn": { "type": "string", "description": "IAM role ARN to assume (cross-account)" }, "external_id": { "type": "string", "description": "External ID for STS AssumeRole" } }, "required": [ "host", "database", "user", "region" ], "additionalProperties": false, "description": "AWS RDS IAM authentication config" }, "ssl_ca_pem": { "type": "string", "description": "PEM-encoded CA certificate for SSL verification (required for verify-ca / verify-full with a private CA)" } }, "additionalProperties": false }, "DestinationGoogleSheetsConfig": { "type": "object", "properties": { "client_id": { "type": "string", "description": "Google OAuth2 client ID (env: GOOGLE_CLIENT_ID)" }, "client_secret": { "type": "string", "description": "Google OAuth2 client secret (env: GOOGLE_CLIENT_SECRET)" }, "access_token": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "refresh_token": { "type": "string", "description": "OAuth2 refresh token" }, "spreadsheet_id": { "type": "string", "description": "Target spreadsheet ID (created if omitted)" }, "spreadsheet_title": { "default": "Stripe Sync", "type": "string", "description": "Title when creating a new spreadsheet" }, "batch_size": { "default": 50, "type": "number", "description": "Rows per Sheets API append call" } }, "required": [ "refresh_token" ], "additionalProperties": false }, "DestinationStripeConfig": { "allOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "oneOf": [ { "type": "object", "properties": { "api_key": { "type": "string", "description": "Stripe API key (sk_test_... or sk_live_...)" }, "base_url": { "type": "string", "format": "uri", "description": "Override the Stripe API base URL (e.g. http://localhost:12111 for tests)" }, "max_retries": { "default": 3, "type": "integer", "minimum": 0, "maximum": 9007199254740991, "description": "Retries for 429/5xx/network errors" }, "api_version": { "type": "string", "const": "unsafe-development" }, "object": { "type": "string", "const": "custom_object" }, "write_mode": { "type": "string", "const": "create" }, "streams": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "object", "properties": { "plural_name": { "type": "string", "description": "Stripe Custom Object api_name_plural" }, "field_mapping": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "string" }, "description": "Mapping from Custom Object field names to source record fields." } }, "required": [ "plural_name", "field_mapping" ], "additionalProperties": false }, "description": "Per-source-stream Custom Object write configuration." } }, "required": [ "api_key", "api_version", "object", "write_mode", "streams" ], "additionalProperties": false }, { "type": "object", "properties": { "api_key": { "type": "string", "description": "Stripe API key (sk_test_... or sk_live_...)" }, "base_url": { "type": "string", "format": "uri", "description": "Override the Stripe API base URL (e.g. http://localhost:12111 for tests)" }, "max_retries": { "default": 3, "type": "integer", "minimum": 0, "maximum": 9007199254740991, "description": "Retries for 429/5xx/network errors" }, "api_version": { "type": "string", "enum": [ "2026-03-25.dahlia", "2026-02-25.clover", "2026-01-28.clover", "2025-12-15.clover", "2025-11-17.clover", "2025-10-29.clover", "2025-09-30.clover", "2025-08-27.basil", "2025-07-30.basil", "2025-06-30.basil", "2025-05-28.basil", "2025-04-30.basil", "2025-03-31.basil", "2025-02-24.acacia", "2025-01-27.acacia", "2024-12-18.acacia", "2024-11-20.acacia", "2024-10-28.acacia", "2024-09-30.acacia", "2024-06-20", "2024-04-10", "2024-04-03", "2023-10-16", "2023-08-16", "2022-11-15", "2022-08-01", "2020-08-27", "2020-03-02", "2019-12-03", "2019-11-05", "2019-10-17", "2019-10-08", "2019-09-09", "2019-08-14", "2019-05-16", "2019-03-14", "2019-02-19", "2019-02-11", "2018-11-08", "2018-10-31", "2018-09-24", "2018-09-06", "2018-08-23", "2018-07-27", "2018-05-21", "2018-02-28", "2018-02-06", "2018-02-05", "2018-01-23", "2017-12-14", "2017-08-15" ] }, "object": { "type": "string", "const": "standard_object" }, "write_mode": { "type": "string", "const": "create" }, "streams": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "object", "properties": { "field_mapping": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "string" }, "description": "Mapping from Stripe create parameter names to source record fields." } }, "required": [ "field_mapping" ], "additionalProperties": false }, "description": "Per-source-stream standard Stripe object create configuration." } }, "required": [ "api_key", "api_version", "object", "write_mode", "streams" ], "additionalProperties": false } ] } ] }, "DestinationRedisConfig": { "type": "object", "properties": { "url": { "type": "string", "description": "Redis connection URL (redis://host:port)" }, "host": { "type": "string", "description": "Redis host (default: localhost)" }, "port": { "type": "number", "description": "Redis port (default: 6379)" }, "password": { "type": "string", "description": "Redis password" }, "db": { "type": "number", "description": "Redis database number (default: 0)" }, "tls": { "type": "boolean", "description": "Enable TLS" }, "key_prefix": { "type": "string", "description": "Prefix for all Redis keys (default: empty)" }, "batch_size": { "default": 100, "type": "number", "description": "Records to buffer before flushing via pipeline" } }, "additionalProperties": false }, "SyncState": { "type": "object", "properties": { "source": { "$ref": "#/components/schemas/SourceState" }, "destination": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {}, "description": "Destination connector state." }, "sync_run": { "type": "object", "properties": { "run_id": { "description": "Identifies a finite backfill run. Omit for continuous sync.", "type": "string" }, "time_ceiling": { "description": "Frozen upper bound (ISO 8601). Set on first invocation when run_id is present; reused on continuation.", "type": "string" }, "progress": { "description": "Accumulated progress from prior requests in this run.", "$ref": "#/components/schemas/ProgressPayload" } }, "required": [ "progress" ], "description": "Engine-managed run state — run_id, time_ceiling, accumulated progress." } }, "required": [ "source", "destination", "sync_run" ], "description": "Full sync checkpoint with separate sections for source, destination, and sync run. Connectors only see their own section; the engine manages routing." }, "SourceState": { "type": "object", "properties": { "streams": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {}, "description": "Per-stream checkpoint data, keyed by stream name." }, "global": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {}, "description": "Source-wide state shared across all streams." } }, "required": [ "streams", "global" ], "description": "Source connector state — cursors, backfill progress, events cursors." }, "RunStatus": { "type": "string", "enum": [ "started", "succeeded", "failed" ], "description": "succeeded = all streams completed/skipped; failed = connection_status failed OR any stream errored." }, "StreamProgress": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "not_started", "started", "completed", "skipped", "errored" ], "description": "Current state, derived from stream_status events." }, "state_count": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Number of state checkpoints for this stream." }, "record_count": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Records synced for this stream in this run." }, "message": { "description": "Human-readable status message (error reason, skip reason, etc).", "type": "string" }, "total_range": { "description": "Full backfill time span for this stream.", "type": "object", "properties": { "gte": { "type": "string", "description": "Inclusive lower bound (ISO 8601)." }, "lt": { "type": "string", "description": "Exclusive upper bound (ISO 8601)." } }, "required": [ "gte", "lt" ] }, "completed_ranges": { "description": "Completed time sub-ranges within the total_range.", "type": "array", "items": { "type": "object", "properties": { "gte": { "type": "string", "description": "Inclusive lower bound (ISO 8601)." }, "lt": { "type": "string", "description": "Exclusive upper bound (ISO 8601)." } }, "required": [ "gte", "lt" ] } } }, "required": [ "status", "state_count", "record_count" ], "description": "Per-stream progress snapshot." }, "ProgressPayload": { "type": "object", "properties": { "started_at": { "type": "string", "description": "When this sync started (ISO 8601); generally equals time_ceiling." }, "elapsed_ms": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Wall-clock milliseconds since the sync run started." }, "global_state_count": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Total source_state messages observed so far." }, "connection_status": { "description": "Set when source or destination emits connection_status: failed.", "type": "object", "properties": { "status": { "type": "string", "enum": [ "succeeded", "failed" ], "description": "Whether the connection check passed." }, "message": { "description": "Human-readable explanation of the check result.", "type": "string" } }, "required": [ "status" ] }, "derived": { "type": "object", "properties": { "status": { "$ref": "#/components/schemas/RunStatus" }, "records_per_second": { "type": "number", "description": "Overall throughput for the entire run." }, "states_per_second": { "type": "number", "description": "State checkpoints per second." }, "total_record_count": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Total records across all streams." }, "total_state_count": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991, "description": "Total source_state messages across all streams." } }, "required": [ "status", "records_per_second", "states_per_second", "total_record_count", "total_state_count" ], "description": "Computed aggregates." }, "streams": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "$ref": "#/components/schemas/StreamProgress" }, "description": "Per-stream progress, keyed by stream name." } }, "required": [ "started_at", "elapsed_ms", "global_state_count", "derived", "streams" ], "description": "Periodic sync progress emitted by the engine as a top-level message. Each emission is a full replacement." }, "Pipeline": { "type": "object", "properties": { "id": { "type": "string", "minLength": 3, "maxLength": 64, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$", "description": "Unique pipeline identifier (e.g. pipe_abc123)." }, "source": { "$ref": "#/components/schemas/SourceConfig" }, "destination": { "$ref": "#/components/schemas/DestinationConfig" }, "streams": { "description": "Selected streams to sync. All streams synced if omitted.", "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "Stream (table) name to sync." }, "sync_mode": { "description": "How the source reads this stream. Defaults to full_refresh.", "type": "string", "enum": [ "incremental", "full_refresh" ] }, "backfill_limit": { "description": "Cap backfill to this many records, then mark the stream complete.", "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 } }, "required": [ "name" ], "additionalProperties": false } }, "desired_status": { "default": "active", "description": "User-controlled lifecycle state. Set via PATCH to pause, resume, or delete.", "type": "string", "enum": [ "active", "paused", "deleted" ] }, "status": { "default": "setup", "description": "Workflow-controlled execution state. Updated by the Temporal workflow.", "type": "string", "enum": [ "setup", "backfill", "ready", "paused", "teardown", "error" ] }, "sync_state": { "description": "Latest full sync checkpoint emitted by the engine. Includes source, destination, and sync-run state for the next request.", "$ref": "#/components/schemas/SyncState" } }, "required": [ "id", "source", "destination", "desired_status", "status" ], "additionalProperties": false }, "EofPayload": { "type": "object", "properties": { "status": { "description": "Terminal run status derived from stream outcomes.", "$ref": "#/components/schemas/RunStatus" }, "has_more": { "type": "boolean", "description": "Whether the client should continue with another request. true when cut off by limits; false when the source iterator exhausted naturally." }, "ending_state": { "description": "Full sync state at the end of this request. Round-trip this as starting_state on the next request.", "$ref": "#/components/schemas/SyncState" }, "run_progress": { "description": "Accumulated progress across all requests in this sync run.", "$ref": "#/components/schemas/ProgressPayload" }, "request_progress": { "description": "Progress for this specific request only.", "$ref": "#/components/schemas/ProgressPayload" } }, "required": [ "status", "has_more", "run_progress", "request_progress" ], "additionalProperties": false, "description": "Deprecated terminal message signaling end of this request. Prefer explicit request/response results via pipeline_sync_batch." } } } }