Format grant 409: exists, self, or workspace_required
A 409 on POST .../grants is format_grant_exists, format_grant_self or format_workspace_required. Each has its own fix, and none is a retry.

A 409 from POST /v1/formats/{handle}/{slug}/grants means the pair is already related or cannot be related. The error.code tells you which of three cases it is: format_grant_exists, format_grant_self, or format_workspace_required. Retrying the same request never changes the outcome.
The three codes
| error.code | Meaning | Fix |
|---|---|---|
| format_grant_exists | That workspace already holds a pending or accepted grant. | Revoke it, then invite again; or PATCH the role if only the role must change. |
| format_grant_self | The workspace you named already owns the Format. | Name the partner workspace, not your own. |
| format_workspace_required | The Format is not owned by a team workspace. Only team Formats can be shared. | Move the Format into a team workspace, then share it. |
Exists: re-invite versus change role
The most common one is format_grant_exists. It fires for a pending grant as well as an accepted one, so an invite your partner never answered blocks a second invite. If the goal is to give an existing partner write access, do not revoke and re-invite, because that resets the accept step. Use the role update call on the same grant instead.
If the goal really is a fresh invite, for example the partner lost the grant id, revoke first and then create again.
# change the role in place instead of re-inviting
curl -sS -X PATCH "https://api.sume.com/v1/formats/acme/product-promo/grants/kiwi" \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"role":"write"}'Self: usually a handle mix-up
format_grant_self appears when the handle you put in workspace resolves to the workspace that owns the Format. It most often comes from a script that reuses the owner handle for both the path and the body.
Workspace required: personal Formats
Sharing is workspace to workspace, so the Format itself must belong to a team workspace. A Format that sits under a personal account cannot be granted to anyone, and the API says so with this code rather than a generic 400.
Reading the error in a script
Branch on error.code, never on the message text, because the message is for humans and may be reworded. A small switch is enough: on format_grant_exists, fetch the roster with GET .../grants and decide between a role PATCH and a revoke; on format_grant_self, log a configuration bug; on format_workspace_required, stop and alert a person, since no API call fixes it.
Every error body also carries a request_id. Keep it in your log line, so a support question about a particular rejection can be answered from the one identifier instead of from timestamps.
One practical habit for onboarding scripts: list before you create. A GET on the grants path costs one call and tells you whether the invite already exists, which avoids the conflict and also shows you the accepted state of earlier invites.
Limits
These three are validation conflicts, so the response carries retryable: false and a fix_input next action in the OpenAPI example. Do not put them behind a backoff loop.
They are different from the 404 workspace_not_found and format_not_found cases, which say the invitee or the Format could not be resolved at all.
Sources
Related posts
More in Formats
- Format run spend caps: the $500 ceiling and the null trap
A Sume Format run cannot spend past its cap. Omit it to inherit the Format's cap, send up to 500 to set one, and know null means $500, not no limit.
- Gemini Omni edit: direct Video Router call or a Sume Format run?
Use the Video Router edit call for one precise change to one clip. Use a Format run when the job has steps, a schema and a spend cap around the edit.
- Spend cap for six 10-second 1080p clips: set $25, not the $400 default
Six 10-second Omni 1080p clips bill $11.28 on Sume. Set generation_spend_cap_usd to about $25 to allow one retake each, instead of the $400 default or $500 max.
- Gift message field in a strict output schema: optional means nullable
In a Sume output_schema every property must be required, so an optional gift message is a string-or-null union, not an omitted key. Here is the shape.
Written by Sume