← All documentation

API reference

The Plugsync Event Ingestion API lets you push events from any system to a connector. Ingestion endpoints require a connector API key sent in the x-api-key header.

Base URL

https://app.plugsync.com

Authentication

Every request must include an x-api-key: <connector_api_key> header. API keys are scoped to a connector (ps_live_...). You can find or rotate them under Connectors → [your connector] → API keys.

curl -H "x-api-key: ps_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  https://app.plugsync.com/api/v1/events/<event_id>/status

POST /api/v1/events

Ingest a single event.

Request body:

{
  "event": "contact_upserted",
  "data": {
    "external_id": "user-42",
    "email": "[email protected]",
    "first_name": "Alice"
  }
}

The business payload goes under data, and any identifier your system uses (such as external_id) lives inside it, not at the top level.

Field Type Required Notes
event string yes Event type; event flows subscribe to it.
data object yes The business payload; addressed as $.payload.* inside flows.
storeId string no Required for multi-store connectors. Identifies the source store.
timestamp string no ISO-8601 timestamp. Defaults to server time.

Response:

{
  "event_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "processing"
}

The response may also carry an advisory hint string — for example when no published event flow subscribes to the event you sent. Such an event is still accepted (202) but nothing is synced: polling GET /api/v1/events/<event_id>/status reports it as skipped, never completed.

Webhook signature verification

Event sources of type webhook receive events at POST /api/webhooks/<connector_id>/<source_name>. This endpoint takes no API key: the HMAC signature is the authentication. The event source’s config.inbound declares the scheme:

{
  "inbound": {
    "verify_signature": "hmac_sha256",
    "signature_header": "x-signature-sha256",
    "encoding": "base64",
    "event_type_path": "$.event_type"
  }
}

verify_signature is the algorithm name (hmac_sha256 or stripe_signing_v1), not a boolean. signature_header, encoding (base64 default, hex opt-in) and event_type_path are optional, with the defaults shown above.

For hmac_sha256, sign the raw request body with your webhook secret (no timestamp is involved) and send the result in the configured header:

SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64)
curl -X POST https://app.plugsync.com/api/webhooks/<connector_id>/<source_name> \
  -H "Content-Type: application/json" \
  -H "x-signature-sha256: $SIG" \
  -d "$BODY"

Equivalent Python:

import base64, hashlib, hmac
signature = base64.b64encode(
    hmac.new(secret.encode(), body_bytes, hashlib.sha256).digest()
).decode()

A misconfigured source answers 400 with error code webhook_source_misconfigured; a wrong or missing signature answers 401.

Rate limits

Two independent limits apply to the Event Ingestion API, both scoped to your organization:

Error responses

Every error response carries a detail field, but its shape depends on the error:

Status error When
401 (plain string) Missing or invalid x-api-key header, e.g. "Missing x-api-key header" or "Invalid or revoked API key".
403 bulk_import_not_enabled Bulk endpoint used but bulk import isn’t enabled for this connector; contact support to enable it.
409 body_mismatch (plain string) Bulk replay: same idempotency_key as an existing job, but a different number of entities.
422 (validation error) Body fails schema validation. FastAPI returns field-level detail (a list of {loc, msg, type}).
423 (plain string) Connector status blocks ingestion, e.g. "Connector is paused, ingestion is blocked".
429 quota_exceeded Monthly event quota exceeded for the org’s tier. Structured, no message key.
429 (plain string) Per-second rate limit hit for the org, e.g. "Rate limit exceeded for org". Separate from the quota above.
500 internal_error Unhandled server error. Safe to retry with backoff.
503 event_ingestion_unavailable Enqueueing the event failed (e.g. the queue was unreachable). Safe to retry.

Every response, success or error, carries an X-Request-ID header: include it when contacting support.

Idempotency

Single events sent to POST /api/v1/events are not deduped: sending the same event/data twice creates two events. For bulk re-syncs, use POST /api/v1/events/bulk with an idempotency_key: replaying the same key returns the original job instead of creating a new one, as long as the replay has the same number of entities as the original request. If the entity count differs, the endpoint returns 409 body_mismatch rather than silently accepting the new payload. The bulk endpoint requires bulk import enabled on the connector (contact support), otherwise it returns 403 bulk_import_not_enabled.