---
title: Generation admission
description: How Sume admits paid generation jobs, applies workspace concurrency, queues work, and reports queue status.
---

Sume generation APIs use queue-first admission for long-running paid work. A
submit request creates a durable job when the request is valid, balance can be
reserved, and the workspace still has accepted-job capacity. The job may start
immediately or wait in `queued` until a workspace concurrency slot opens.

```text
submit
  -> queued
  -> processing
  -> completed | failed | canceled
```

Concurrency is a dispatch limit, not a submit limit. If your workspace is
already at its generation concurrency limit, Sume can still accept more jobs as
`queued` while queue capacity remains. Workers later move queued jobs to
`processing` under the per-workspace concurrency guard.

## Limits at a glance

Sume separates four controls that are easy to confuse:

| Control | Applies to | What happens when full |
|---|---|---|
| Generation concurrency | Paid generation jobs with status `processing`. | New valid jobs can still be accepted as `queued` if queue capacity remains. |
| Queue capacity | Paid generation jobs accepted but not yet processing. | New paid generation submissions fail with `429 queue_full`. |
| Submit rate limits | Request volume for public API submit endpoints. | Requests fail with `429 rate_limited`; retry with backoff and an idempotency key. |
| Balance and reservation | Spendable USD balance for the authenticated workspace. | Generation submit fails with `402 insufficient_credits` before provider work starts. |

Read/status/list endpoints can also have rate limits. Treat those as polling
backpressure, not generation concurrency.

## Processing concurrency

Generation concurrency is **plan-only**. Prepaid top-ups do **not** raise the
processing concurrency limit. Admin overrides may raise the effective
`concurrency_limit` (`limit_source: admin_override`). Queue capacity defaults to
`max(3, concurrency_limit × 5)`.

| Plan | Processing concurrency | Queue capacity (default) | Accepted job capacity |
|---|---:|---:|---:|
| Free | 1 | 5 | 6 |
| Pro | 4 | 20 | 24 |
| Startup | 8 | 40 | 48 |
| Scale | 20 | 100 | 120 |
| Enterprise | 20 | 100 | 120 |

The dashboard **Concurrency** tab is the source of truth for the workspace's
configured processing cap, exposed as `generation_limits.concurrency_limit`.
Org workspaces have a floor of 10; Enterprise defaults to 20 and uses admin
overrides for higher contract limits. Always prefer the effective field over
this static table. `accepted job capacity` is
`concurrency_limit + queued_jobs_limit` — the maximum number of paid generation
jobs that can be `processing` or `queued` for the workspace at the same time.

## Queue-first behavior

If your workspace has `concurrency_limit: 1`, you may submit several valid jobs
at once. Sume can return all of them as `queued` as long as balance and queue
capacity are available. Only one same-workspace generation job should move to
`processing` at a time.

This is the intended behavior:

```text
Job A: queued -> processing -> completed
Job B: queued -------------> processing -> completed
Job C: queued ---------------------------> processing -> completed
```

Do not treat `queued` as failure. Store the `job_id`, poll status with backoff,
and fetch the result only when the job reports `result_ready: true` or
`status: completed`.

## Immediate rejection

Sume rejects immediately only when the request cannot be safely accepted.

| Status | Code | Why it happens | Client behavior |
|---|---|---|---|
| `400` | `invalid_request` | The request body, model id shape, mode, webhook options, or headers are invalid. | Fix the request before retrying. |
| `401` | `unauthorized` | The API key is missing, malformed, revoked, or invalid. | Fix authentication. |
| `402` | `insufficient_credits` | Sume cannot reserve the estimated generation cost from the workspace balance. | Upgrade the plan / wait for included Gen$, or submit a cheaper request. Do not invent prepaid top-ups. |
| `404` | `model_not_found` or `not_found` | The public model or resource does not exist in this workspace. | Use `/v1/catalog` or verify ids. |
| `409` | `idempotency_conflict` | The same idempotency key was reused for a different operation or payload. | Reuse keys only for exact retries. |
| `429` | `queue_full` | The workspace has no remaining accepted generation capacity. | Wait for jobs to finish or cancel queued jobs, then retry with the same idempotency key. |
| `429` | `rate_limited` | API request volume exceeded an abuse-protection limit. | Back off using `retry-after` when present. |
| `503` | `provider_capacity_exceeded` or runtime configuration errors | Sume cannot start or dispatch generation work safely. | Retry later with the same idempotency key unless the error says not to retry. |

Concurrency being full is not an error by itself. It becomes a submit error only
when the queue is also full.

## `generation_limits`

Generation submit responses include `generation_limits` when Sume can compute
the workspace admission snapshot.

```json
{
  "generation_limits": {
    "plan_id": "pro",
    "limit_source": "plan",
    "plan_concurrency_limit": 4,
    "concurrency_limit": 4,
    "queued_jobs_limit": 20,
    "accepted_generation_jobs_limit": 24,
    "active_generation_jobs": 0,
    "queued_generation_jobs": 0,
    "queue_capacity_remaining": 24,
    "wave_size_hint": 18
  }
}
```

Field meanings:

| Field | Meaning |
|---|---|
| `plan_id` | Subscription plan that sets the default concurrency map. |
| `limit_source` | `plan` or `admin_override` for the effective concurrency. |
| `plan_concurrency_limit` | Plan-default processing concurrency (ignore for wave sizing when overridden). |
| `concurrency_limit` | **Effective** maximum same-workspace paid generation jobs that can be `processing`. |
| `queued_jobs_limit` | Additional same-workspace paid generation jobs that can wait in `queued`. |
| `accepted_generation_jobs_limit` | `concurrency_limit + queued_jobs_limit`. |
| `active_generation_jobs` | Current same-workspace generation jobs with status `processing`. |
| `queued_generation_jobs` | Current same-workspace generation jobs with status `queued`. |
| `queue_capacity_remaining` | Remaining queued-job budget plus idle processing seats, before `queue_full`. |
| `wave_size_hint` | Submission-wave hint only: `max(1, floor(queue_capacity_remaining * 0.75))`. Not a concurrency limit, override, or processing width; never use it to size in-flight work. |

The counts are a snapshot. They can change immediately after the response as
workers claim jobs or other clients submit work.

## Sizing in-flight work

Use `max(0, concurrency_limit - active_generation_jobs - queued_generation_jobs)`,
capped by `queue_capacity_remaining`, as the budget for new in-flight work.
Count each newly submitted job against that budget until the next live snapshot.
At zero headroom, wait and refresh the preview before submitting more; if counts
are unavailable, refresh before choosing a width. This client pacing keeps
outstanding work within the processing cap; the API can still accept queued
work under its separate queue-first admission policy.

For example, `concurrency_limit: 100`, `queued_jobs_limit: 500`, and no active
or queued jobs give `queue_capacity_remaining: 600` and `wave_size_hint: 450`.
The workspace is set to **100**, with at most 100 new in-flight jobs in this
snapshot. **450 is only a submission-wave hint** including queue slots. With
30 processing and 10 queued, the new in-flight budget is 60. Never present the
hint as concurrency or substitute the pre-override `plan_concurrency_limit` /
`purchased_concurrency_limit` fields. A full queue still means wait even though
the hint's minimum value is 1.

## Before bulk submissions

For launch integrations, use `GET /v1/balance` and the `generation_limits`
returned by generation submit responses to make conservative queue decisions.
If the live OpenAPI later exposes a read-only admission preview endpoint for
your environment, treat it as optional preflight only: it must not create a job,
reserve credits, capture usage, refund usage, or call generation providers.

## Submit and poll pattern

For production integrations, prefer async submit with an idempotency key.

```bash
curl -X POST https://api.sume.com/v1/avatar-1.0/generate \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: avatar-batch-001-item-001" \
  -d '{
    "avatar_handle": "studio_presenter",
    "input": {
      "type": "prompt",
      "prompt": "Friendly studio presenter"
    },
    "mode": "async"
  }'
```

Then poll status:

```bash
curl https://api.sume.com/v1/jobs/job_123/status \
  -H "Authorization: Bearer $SUME_API_KEY"
```

Fetch the result after completion:

```bash
curl https://api.sume.com/v1/jobs/job_123/result \
  -H "Authorization: Bearer $SUME_API_KEY"
```

Recommended client behavior:

- Treat `queued` and `processing` as normal non-terminal states.
- Use exponential backoff for polling. Avoid tight loops across many jobs.
- Keep polling until `terminal: true`, or until your own application deadline.
- Use `Idempotency-Key` for every paid submit that may be retried.
- Do not resubmit a paid request just because your local worker timed out.
- Store `status_url`, `result_url`, `events_url`, and `cancel_url` when present.
- Check `generation_limits` and stop adding work when queue capacity is low.

The Sume CLI follows the same model:

```bash
sume avatars create --confirm-paid --avatar-handle studio_presenter --type prompt --prompt "Friendly studio presenter" --json
sume jobs status job_123 --agent --json
sume jobs result job_123 --agent --json
sume jobs events job_123 --agent --json
```

## Queue-full handling

`queue_full` means the workspace has consumed all accepted generation capacity:

```json
{
  "error": {
    "code": "queue_full",
    "message": "Workspace generation queue is full. Wait for running jobs to complete before submitting more generation work.",
    "request_id": "req_..."
  }
}
```

The error details can include a `generation_limits` snapshot and job metadata
for the failed admission attempt. Sume releases or refunds the reservation for
the failed admission when applicable.

When you receive `queue_full`:

- stop adding more generation work for that workspace;
- poll existing jobs until at least one reaches a terminal state;
- cancel queued jobs that are no longer needed;
- retry with the same idempotency key after capacity opens;
- use `retry-after` when present.

## Cancellation and billing

Paid generation uses public Sume USD estimates. At submit time, Sume reserves
the estimated amount when the request is accepted. Successful completion captures
the reserved usage. Failed jobs and failed queue admission release or refund the
reservation where applicable.

Cancellation is available for jobs that are still `queued` or `processing`:

```bash
curl -X POST https://api.sume.com/v1/jobs/job_123/cancel \
  -H "Authorization: Bearer $SUME_API_KEY"
```

Cancel queued jobs you no longer need before they start processing. Once a job
is already `processing`, cancellation is best-effort and the job may still
complete or fail normally.

## Edge cases and current boundaries

- Sume currently exposes queue counts and remaining accepted capacity, not a
  precise per-job queue position or ETA.
- `sync` and `subscribe` modes can wait up to 30 seconds. If the wait budget is
  exhausted, keep polling the job id.
- Queue expiration and explicit client-supplied fail-fast queue length are not
  currently public API options. Treat them as future contract additions unless
  they appear in the live OpenAPI schema.
- Public API responses are provider-neutral. They do not expose hidden provider
  names, raw provider task ids, raw provider URLs, internal workflow names,
  storage object keys, API keys, or private workspace/user metadata.
