Integrations

Partner API reference

Every operation the partner API offers, with the scopes, request shapes, and rules a client has to follow.

On this page

The partner API is a versioned, server-to-server REST API at https://integrations.snapbar.com/v1, authenticated with an API key as a bearer token. The OpenAPI document is the canonical reference for parameters, bodies, and schemas; this page explains the rules around them. Start with Create API keys.

Read activations and submissions

  1. GET /activations lists the activations (experiences) in the key account, newest first, with id, short_id, name, type, state, starts_at, ends_at, and created_at. The id is the activation identifier every other route takes. Scope: read submissions. A full example page is in Create API keys.

  2. GET /activations/{id}/submissions lists completed submissions as envelopes with visible finished media. submission.approval_status is pending, approved, or rejected; add approved_only=true for a display or approval-aware catch-up. Approval and completion are different: a generic completed list may contain unreviewed photos. Hidden media is omitted. Without include_lead=true, the lead key is left out. An unknown or foreign activation answers resource_not_found. Scope: read submissions.

  3. For incremental polling, choose a fixed completed_after lower bound and keep it unchanged while draining every page via next_cursor, including pages of equal timestamps. Only after has_more=false persist the greatest observed submission.completed_at. Start the next scan before that watermark (for example, five minutes earlier) and upsert by submission/media ID. The filter is strictly greater-than, so advancing it between pages loses equal-timestamp rows. Bounded overlap cannot guarantee capture of arbitrarily late commits or later approval/hide/delete changes: periodically drain a full scan without completed_after, and reconcile local membership only after the complete scan succeeds. The runnable poller linked below implements these rules. Without the parameter, results remain newest first.

    curl --fail-with-body --get \
      --header "Authorization: Bearer ${SNAPBAR_API_KEY}" \
      --data-urlencode "completed_after=2026-07-18T19:00:00Z" \
      --data-urlencode "limit=100" \
      https://integrations.snapbar.com/v1/activations/${ACTIVATION_ID}/submissions
  4. GET /submissions/{id} returns any submission state, current approval_status, and nullable external_id. Pending/processing/failed submissions have no finished media and completed_at: null; poll after an ingest 202 until complete. For an approval-gated display, require both state: complete and approval_status: approved, and use only the returned visible media. A missing/deleted submission answers resource_not_found; remove its cached copy. Scope: read submissions.

  5. Add include_lead=true to any read to include contact details, custom fields, and consent. That needs the read lead details preset and a live key, and returns leads regardless of marketing consent: consent gates what Snapbar pushes to a destination, not what the owning account may read about its own guests.

Send photos in

  1. POST /activations/{id}/submissions creates a submission from one to three public HTTPS image links. Scope: create submissions. Every accepted request spends one activation credit. An Idempotency-Key header of up to 255 characters is required; see the rules below before choosing one.

    curl --fail-with-body \
      --request POST \
      --header "Authorization: Bearer ${SNAPBAR_API_KEY}" \
      --header 'Content-Type: application/json' \
      --header "Idempotency-Key: ${YOUR_REQUEST_ID}" \
      --data '{
        "media_urls": ["https://media.example.com/source.jpg"],
        "processing": "ai",
        "locale": "en",
        "input_data": {"campaign": "summer-launch"},
        "lead_data": {"email": "guest@example.com", "terms_consent": true, "marketing_consent": false}
      }' \
      https://integrations.snapbar.com/v1/activations/${ACTIVATION_ID}/submissions
  2. The response reports the activation, the style that ran, the processing mode, and the new submission. lead_persisted is true when your lead_data was stored, null when you sent none, and false when the photo landed but the lead could not be saved; a 201 or 202 with false means the submission exists, the credit is spent, and you should resend the contact details through your own channel because the request will not be retried for you.

    HTTP/1.1 202 Accepted
    X-Request-Id: c0a8012e-4f2b-4b7d-9c1e-6d3a8f5b2e10
    
    {
      "activation_id": "2f5c1a8e-7d3b-4e1f-9a6c-0b8d4e2f1a37",
      "definition_key": "style_1",
      "lead_persisted": true,
      "processing": "ai",
      "submission": {
        "id": "6b1d9e3c-2a4f-4c8e-b7d1-5f0a3e9c2b48",
        "short_id": "sub_Sub123Def456",
        "state": "pending"
      }
    }
  3. processing is ai by default: the images are inputs, the activation style runs, and the response is 202 with state pending. upload_only treats the images as the finished result and returns 201 with state complete. Both consume one credit; with none available the API answers insufficient_credits.

  4. Before fetching any image, Snapbar checks that the activation exists in the account, is active and unlocked with a published release, that definition_key names an active style, and that a credit is available, in that order. A request that fails those checks is refused with resource_not_found, activation_not_accepting, or insufficient_credits before any media is downloaded, so it costs nothing and returns quickly.

  5. Idempotency keys are account-scoped and shared by every key in that account: namespace them by application. Repeat the same key and body after an uncertain response; never mint a new key to retry that photo. A stored success replays its original response. 503 ingest_outcome_unknown means the commit outcome is not yet known: wait for Retry-After and retry unchanged. A 409 idempotency_conflict that carries Retry-After means the original request is still in flight (for up to ten minutes): keep retrying the same key and body. A 409 idempotency_conflict without Retry-After means the body changed. A confirmed rejection releases the claim. Success replay expires after 24 hours, after which reuse may create a second submission and charge again; reconcile by your saved submission/external ID before retrying an older request.

  6. Each image may be up to 20 MB and must be a PNG, JPEG, GIF, or WebP; the file bytes are checked, not just the extension. Snapbar fetches it through a protected fetcher that follows no redirects and gives up after 15 seconds, then rehosts it before storing the submission. A fetch, type, size, or rehosting failure returns media_unfetchable. Video, HEIC, and other media types are not accepted.

  7. GET /activations/{id}/styles lists active styles in the published release with definition_key, name, pipeline_type, prompt_max_length, and output_count. Scope: read submissions. Use the returned key for ingest; omit it to use the first active published style. An unknown or inactive key answers resource_not_found. The list contains public style metadata, not the private prompt or workflow.

  8. lead_data is optional. Any contact field or custom field requires terms_consent: true, and the consent source is recorded as api. locale is a BCP 47 tag such as en or pt-BR and controls the language of the result email and text message; omitted, delivery uses the activation default.

  9. input_data remains write-only job metadata; keep your own copy. Use the separate top-level external_id (an opaque string of up to 255 characters, without personal data) to correlate your application: it is echoed by ingest and returned as submission.external_id in reads and webhooks. It is not unique and does not replace Idempotency-Key. Reserved input_data.prompt overrides the AI style prompt; whitespace means unset. The selected published style’s prompt_max_length is enforced before image fetching or credit reservation: Seedream 4.5 and Seedream 5 Pro allow 4,000 characters; compatible image models allow up to 10,000. Reserved input_data.locale overrides top-level locale; both are BCP 47 language tags. Do not use these names for unrelated metadata.

Moderate media

  1. PATCH /media/{id} hides or restores one media item. Scope: moderate submissions. The change also applies to every item in the same submission whose address is byte-identical, such as a passthrough input and output pair, and never to another submission. Hidden media disappears from later reads and from Snapbar displays; deliveries already sent are not recalled. A hide made through the API is recorded as an API hide, so a later dashboard approval does not undo it; only your tools or a moderator restore it.

    curl --fail-with-body \
      --request PATCH \
      --header "Authorization: Bearer ${SNAPBAR_API_KEY}" \
      --header 'Content-Type: application/json' \
      --data '{"is_hidden": true}' \
      https://integrations.snapbar.com/v1/media/${MEDIA_ID}

Deliveries

  1. GET /deliveries lists deliveries newest first with optional status, experience_id, and endpoint_id filters. status takes one or more of pending, paused, delivering, succeeded, failed_retryable, failed_permanent, unknown, skipped_consent, skipped_no_contact, and skipped_disabled, comma-separated or repeated, at most 10 per request. The table below maps each value to the label in Activity. Scope: operations.

    curl --fail-with-body --get \
      --header "Authorization: Bearer ${SNAPBAR_API_KEY}" \
      --data-urlencode "status=failed_retryable,failed_permanent" \
      --data-urlencode "endpoint_id=${ENDPOINT_ID}" \
      https://integrations.snapbar.com/v1/deliveries
  2. Each row carries the ids you need to join it to a submission and an endpoint or destination, the attempt count, the next scheduled attempt, and the class of the last error. origin is live for a delivery created by guest activity and backfill for one queued by Send finalized leads; destination_kind is webhook_endpoint or connection, and destination_label is the receiver host for a webhook.

    {
      "id": "0d7f3a2b-9c1e-4b6d-8a5f-3e2c1b0a9d8e",
      "account_id": "…",
      "experience_id": "2f5c1a8e-7d3b-4e1f-9a6c-0b8d4e2f1a37",
      "submission_id": "6b1d9e3c-2a4f-4c8e-b7d1-5f0a3e9c2b48",
      "destination_kind": "webhook_endpoint",
      "destination_label": "example.com",
      "endpoint_id": "9c2e4b6a-1d3f-4a5b-8c7d-2e1f0a9b8c7d",
      "binding_id": null,
      "binding_config_version": null,
      "connection_id": null,
      "trigger_event": "submission.completed",
      "origin": "live",
      "status": "failed_retryable",
      "attempts": 2,
      "last_error_class": "http_503",
      "next_attempt_at": "2026-07-18T19:30:35.000+00:00",
      "delivering_started_at": null,
      "completed_at": null,
      "idempotency_key": "…",
      "provider_object_ids": null,
      "created_at": "2026-07-18T19:24:35.512+00:00"
    }
  3. POST /deliveries/{id}/replay queues a delivery again. Eligible states are failed_retryable, failed_permanent, skipped_disabled, and unknown; anything else answers delivery_not_replayable. An unknown outcome needs {"verified": true} to confirm you checked that the receiver never processed the original attempt. A replay during a live retry supersedes the pending retry rather than racing it. Scope: operations.

Webhook endpoints

  • GET /webhook-endpoints, POST /webhook-endpoints, and DELETE /webhook-endpoints/{id} list, register, and remove signed endpoints. Registration answers 201 with the endpoint and its one-time signing_secret; the full response and the receiver contract are in Receive signed webhooks at your own endpoint. Scope: manage notification endpoints.

  • Deleting an endpoint answers 204 and prevents new sends. Pending and retryable work closes as skipped_disabled; an attempt already in flight settles through its owned outcome or stale-attempt recovery. Deletion cannot recall an HTTP request already sent. Terminal history remains, and a replacement does not inherit old deliveries. Automatic disablement also closes queued work without recalling in-flight requests.

  • GET /hooks/sample?event_type=… returns one synthetic envelope in an array. POST /test/deliveries requires a live key and endpoint_id, then signs a synthetic send with that registered endpoint’s existing secret. Both need the manage notification endpoints preset; see Create API keys for what a test key can do.

  • The OpenAPI document also lists hook subscription operations reserved for automation partners. They are not intended for direct use.

Pagination

  1. Collections return data, next_cursor, and has_more. limit defaults to 20 and is capped at 100. Results are newest first unless completed_after is set. Pass next_cursor back unchanged; keep the same collection and filters for the whole scan. Malformed tokens and tokens from the other pagination mode return invalid_cursor; tokens do not enforce collection or filter identity, so changing either mid-scan can skip records.

    curl --fail-with-body --get \
      --header "Authorization: Bearer ${SNAPBAR_API_KEY}" \
      --data-urlencode "limit=100" \
      --data-urlencode "cursor=${NEXT_CURSOR}" \
      https://integrations.snapbar.com/v1/deliveries

Delivery status values and Activity labels

The status value on a delivery row, the label the Activity tab shows for it, and what it means. The tab is described in Fix failed deliveries.

Sending

pending, delivering
  • pending is queued and waiting for a send slot; delivering is in flight right now.
  • Not replayable.

Retrying

failed_retryable
  • A temporary failure with attempts left on the retry ladder; next_attempt_at says when.
  • Replayable, which supersedes the pending retry.

Action needed

failed_permanent
  • A permanent failure, or the ninth attempt failed. last_error_class names the cause.
  • Replayable after the cause is fixed.

Needs verification

unknown
  • Snapbar cannot prove whether the receiver processed the request.
  • Replayable only with {"verified": true}.

Skipped with reason

skipped_consent, skipped_no_contact, skipped_disabled
  • skipped_consent lacked the required consent; skipped_no_contact had no email for a managed destination; skipped_disabled covers a deleted or auto-disabled endpoint and an approval retracted before delivery (last_error_class says which).
  • Only skipped_disabled is replayable, and a retracted approval revives on its own.

Paused

paused
  • The destination was paused or the account switch was off when the delivery was claimed.
  • Not replayable; it resumes when the switch returns.

Delivered

succeeded
  • The receiver or provider accepted the delivery.
  • Not replayable.

What to know

  • Versioning.The version is part of the path. Within /v1, Snapbar may add optional request fields, response fields, event types, and enum values; existing fields never change meaning. Clients must ignore unknown fields and tolerate new enum values. A breaking change gets a new version, and a deprecated version carries Deprecation and Sunset headers for at least six months before removal.
  • Envelope versions.Webhook deliveries and API reads share Lead Envelope version 1, which is additive only. Each endpoint stores a payload version; when a new version ships, existing endpoints stay pinned and both versions are published for at least six months.
  • Retention and deletion.Ninety days after a delivery finishes, its stored payload is cleared while the row and its status remain; a replay after that point is rebuilt from current data. The ingest idempotency store keeps a successful response for 24 hours. When a submission is erased, personal fields are scrubbed from stored envelopes and error details, media links stop working, and a non-personal tombstone remains for audit. A system that copied lead or media data must honor its own deletion obligations.
  • Errors.Every error response has a stable shape with a code for program logic, a message for diagnostics, a doc_url that links to the matching section of Partner API errors and limits, and a request_id to quote when contacting Snapbar.

Tips

  • Prove the registered receiver with a signed synthetic delivery first; test and live key capabilities are spelled out in Create API keys.
  • Store the submission short_id with anything you keep; it is the identifier staff see in the dashboard.

Related articles

Create API keys for the partner APIReceive signed webhooks at your own endpointPartner API errors and limitsGet started with integrations