HTTP API

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.

← Back to Docs

Overview

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.

Base URL

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).

Versioning (/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.

Management API

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.

Routes

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

Validation errors

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/health

Liveness-style check. No authentication.

Response 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.

Headers

  • 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

Authentication

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.

Request body

  • Either a single JSON object, or a JSON array of objects.
  • Primitives, arrays of non-objects, or empty arrays are rejected.
  • Each object is one record. The server may add fields when pipe enrichment is enabled (ingestion timestamp and/or client IP using configured field names).

Limits

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.

Success 202 Accepted

Records 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"
}

Example (curl)

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.

Ingestion API errors (JSON)

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