Async

Introduction

Sources can accept work asynchronously. The caller gets 202 Accepted with a job id and poll URL. The job still competes for the source's concurrency limit. When the upstream finishes, swarmvia posts a signed webhook and the poll endpoint returns the result.

Async needs at least one active webhook destination. Enable async on Sources → Concurrency, then attach a destination from Webhooks. Sync proxy stays the default. Team and Business plans only.

Admission modes

Mode How it triggers reason on poll / webhook
Header X-Swarmvia-Async: 1 on a normal source request requested
On open wait Request would wait for a slot → accept as a job instead of holding the connection busy
On queue timeout Open wait hits queue timeout → accept as a job instead of 504 queue_timeout wait_timeout

All three share one job pipeline. Slot timing: the concurrency slot is held only while the upstream fetch runs (not while the job waits in the queue).

Async bypasses the shared response cache. Locks stay sync-only.

No active destination

A source with async on but no active destination falls back to sync. That covers both cases: nothing attached yet, and every attached webhook deactivated after repeated delivery failures.

In that state the source behaves exactly as it would with async off:

  • X-Swarmvia-Async: 1 returns the upstream response, not 202.
  • On open wait and on queue timeout do not promote. A request waits for a slot as usual and gets 429 or 504 queue_timeout on its own terms.

No jobs are created while a source is in this state, so nothing sits unclaimed. Attach a working destination (or re-enable a deactivated one) and the next request goes back through the async path.

A source that never enabled async still rejects X-Swarmvia-Async: 1 with 400 async_disabled.

Accept response

{
  "jobId": "job_…",
  "status": "accepted",
  "pollUrl": "https://api.swarmvia.cloud/pools/your-source/jobs/job_…",
  "webhook": { "destinations": 2 }
}

pollUrl is https://api.swarmvia.cloud/pools/{source}/jobs/{jobId}, the same API host as concurrency locks, not your source's endpoint or a custom domain. {source} is your source subdomain. Job ids are job_ followed by hex. Database sources are the exception: their handles poll on the source's own endpoint — see Database sources.

Poll

GET (or HEAD) the pollUrl with the same source auth as a normal request or a lock call. Poll is there for callers that cannot receive a webhook. When destinations are configured, the webhook remains the primary completion signal.

Accept is 202 with status: "accepted". Poll always returns 200 for a known job, with status set to the current lifecycle value:

accepted → queued → running → terminal completed | failed | expired

There is no job-level pending.

Case HTTP Body
Unknown or wrong-source job 404 { "error": "job_not_found" }
Past expiresAt, still non-terminal 200 status: "expired"
Terminal job still in store 200 that terminal status

Every poll body includes jobId, status, reason, createdAt, updatedAt, and expiresAt. Terminal jobs may include result (upstream status, headers, size-capped body). When destinations are on the job, webhook reports destinations, delivered, and failed counts.

Retention

Job retention defaults to 86400 seconds (24 hours). Configurable per source from 60 seconds to 604800 (7 days).

Webhooks

See Webhooks for destinations, signing, and retries.

Limitations

  • On API sources, async requires at least one active webhook destination; without one the source serves sync. Database sources return handles with async enabled alone.
  • Max request body retained for a job defaults to 1 MiB (configurable per source).
  • Job retention defaults to 24 hours (configurable from 60 seconds to 7 days).
  • Webhook delivery is at-least-once; receivers must be idempotent on jobId.
  • Destination count per source is plan-capped.

Next steps