Public JSON endpoints: ingestion, health, and (with a workspace API key) programmatic pipe and destination management. The web app still offers the same operations with session sign-in for humans.
This page is a narrative reference (human-readable, grouped by topic). The closest thing to a universal standard for HTTP APIs is OpenAPI (formerly Swagger): a YAML or JSON description of paths, parameters, request bodies, responses, and security schemes. Tools generate interactive explorers, client stubs, and contract tests from it. We do not publish an OpenAPI document yet; this HTML doc is the source of truth until we add one.
For long pages, a table of contents with in-page anchors (like the sidebar here) is the usual pattern—same idea as MDN, Stripe, or GitHub’s REST docs.
Route anchors: each HTTP route has a stable fragment #route-{METHOD}-v1-… (see the management routes table and ingest sections below). The JavaScript SDK reference links those HTTP: lines here when you regenerate it with npm run docs:sdk.
On this deployment, the HTTP API lives at https://www.batchpipe.com. Paths below are rooted at that origin (set BASE_URL in the server environment so it matches what you use in clients).
/v1)
All documented paths use the /v1/ prefix. That is a common convention for public HTTP APIs: it namespaces the contract so you can ship /v2/ later without colliding with marketing pages or legacy paths. It is not strictly required for a private service, but it keeps ingest, health, and management under one clear API surface.
These routes return JSON and use the same active workspace API key as ingestion: Authorization: Bearer … on every request. They reuse the same validation rules as the dashboard (Zod schemas), but unlike HTML form posts they never use cookies or redirects.
Security: a key that can create pipes, destinations, and sibling API keys is much more powerful than ingest-only. Treat it like a root credential for that workspace, rotate on leak, and prefer separate keys per environment when you can.
Website routes vs API: the dashboard uses session auth and POST to paths like /pipes/create. Management endpoints below are different URLs and verbs (REST-style) but call the same underlying services.
Each row is a deep link: fragment #route-{METHOD}-v1-… — uppercase HTTP verb, path with / turned into -, and :pipe_id style parameters as pipe_id (no colon). Example: GET /v1/pipes/:pipe_id → #route-GET-v1-pipes-pipe_id. Click a path to jump here; copy from the address bar to share.
| Method | Path | Notes |
|---|---|---|
| GET | /v1/pipes | List pipes + usage snapshot |
| POST | /v1/pipes | Create pipe (body matches pipe create form) |
| GET | /v1/pipes/:pipe_id | Pipe detail; destination secrets redacted as [redacted] |
| PATCH | /v1/pipes/:pipe_id | Update pipe (same fields as dashboard settings) |
| DELETE | /v1/pipes/:pipe_id | Delete pipe |
| POST | /v1/pipes/:pipe_id/destinations | Create destination |
| PATCH | /v1/pipes/:pipe_id/destinations/:destination_id | Update destination; send empty strings for secrets to keep existing values |
| PATCH | /v1/pipes/:pipe_id/destinations/:destination_id/status | Body {"destination_status":"active"|"blocked"} |
| DELETE | /v1/pipes/:pipe_id/destinations/:destination_id | Remove destination |
| PUT | /v1/pipes/:pipe_id/limits | Upsert pipe limits (same numeric fields as dashboard) |
| POST | /v1/pipes/:pipe_id/allowed-origins | Body {"origin_domain":"https://app.example.com"} |
| DELETE | /v1/pipes/:pipe_id/allowed-origins/:origin_id | Remove allowed origin |
| GET | /v1/api-keys | List workspace API keys (metadata only) |
| POST | /v1/api-keys | Create key; response includes full secret once |
| DELETE | /v1/api-keys/:api_key_id | Revoke key; cannot revoke the key used for the request |
Failed body validation returns 400 with error: "validation_error", a message, and an errors object keyed by dot-joined Zod paths (values are human-readable strings). Full examples, redaction rules, Postgres config, and automation guidance: Management API — contract & automation. Machine-readable bundle: OpenAPI YAML.
GET /v1/healthLiveness-style check. No authentication.
200{
"status": "ok",
"timestamp": "2026-04-12T12:00:00.000Z"
}
POST /v1/ingest/:pipe_id
Send one or more JSON records into an active pipe. Replace :pipe_id with the pipe’s UUID (primary key), shown in the dashboard.
Content-Type: application/json (required)Authorization: Bearer <api_key> when the pipe requires an API key (default)Origin for browser CORS: required for CORS-only pipes and recommended for cross-origin API-key ingest when the pipe uses origin checks or open CORS reflection
API key required (typical): the Bearer token must be an active workspace API key for the same workspace as the pipe. If the pipe has allowed browser origins configured and not enabled for any browser origin, requests that send an Origin header must match an allowed origin or the server responds with 403. When any browser origin is enabled on the pipe (dashboard or management API), a standard http(s) Origin is accepted for CORS as long as the API key is valid; allowed-origin rows are not used for that check. On success, responses set Access-Control-Allow-Origin to that same Origin value (echo), not *.
API key not required (CORS-only): either configure at least one allowed origin and send a matching Origin, or enable any browser origin so any standard http(s) Origin is accepted (no API key; treat the ingest URL as a public capability if it leaks). Successful responses echo Origin in Access-Control-Allow-Origin the same way. Intended for browser calls, not as a secret for server-to-server traffic.
If the pipe has a pipe_limit row, the number of records in a single request cannot exceed pl_ingest_max_records_per_request. Otherwise the server returns 413 with error batch_too_large.
202 AcceptedRecords are accepted into the buffer (NATS JetStream). Delivery to your destinations happens asynchronously.
{
"accepted": true,
"pipe_id": "…uuid…",
"pipe_name": "my_pipe",
"records_count": 1,
"request_id": "…uuid…",
"message": "Records accepted for processing"
}
curl -X POST "https://YOUR_HOST/v1/ingest/PIPE_UUID" \
-H "Authorization: Bearer bp_v1_…" \
-H "Content-Type: application/json" \
-d '{"event":"signup","user_id":"123"}'
GET /v1/ingest/:pipe_id
Does not ingest data. Returns JSON explaining that ingestion requires POST with a JSON body (useful when someone opens the URL in a browser).
Statuses: 200 (hint JSON), 400 invalid UUID, 404 unknown or inactive pipe, 500 server error.
OPTIONS /v1/ingest/:pipe_id
CORS preflight. The Origin header must be present. When any browser origin is enabled, a standard http(s) origin succeeds without an allowlist entry; otherwise the origin must match an allowed origin for the pipe. On success the response has no body (204) and echoes Origin in Access-Control-Allow-Origin when allowed. Allowed Access-Control-Allow-Headers include Content-Type and, when the pipe requires an API key, Authorization.
If the origin is missing or not allowed: 400 or 403 with an empty body.
Ingest responses use error (machine-readable code) and message (human-readable). Some responses include extra fields as noted.
| HTTP | error |
When |
|---|---|---|
| 400 | invalid_pipe_id | pipe_id is not a valid UUID |
| 400 | invalid_payload | Body is not a JSON object or array of objects |
| 400 | empty_payload | Empty array or unusable body |
| 401 | unauthorized | Missing/invalid API key (key required), or missing Origin (CORS-only pipe) |
| 403 | forbidden | Origin not allowed for the pipe |
| 404 | pipe_not_found | No active pipe for that UUID |
| 413 | batch_too_large | More records in one request than pl_ingest_max_records_per_request (when a pipe_limit row exists) |
| 500 | configuration_error | CORS-only pipe with neither allowed origins nor “any browser origin” enabled (misconfiguration) |
| 500 | server_error | Unexpected failure or auth subsystem error |
| 503 | buffer_unavailable | Could not enqueue (e.g. NATS JetStream publish failed). Body includes request_id; safe to retry |