Format run 401 with a valid key: Bearer and x-api-key both sent

A Sume Format run returns 401 unauthorized when a request carries two credentials at once. Send Authorization Bearer or x-api-key, never both.

5 min readSume
All posts

A Sume Format run answers 401 unauthorized when the request carries two credentials at once. The call contract allows exactly one of Authorization: Bearer $SUME_API_KEY or x-api-key: $SUME_API_KEY, never both, and the fix lives in the headers, not in the body.

The key itself can be perfectly valid. That is what makes this 401 confusing: you paste the same key into a working curl command and it runs, then your application fails with the same key. The difference is usually one extra header added by a client, wrapper or proxy layer. This post shows how to confirm that and how to make the call send a single credential.

What exactly makes a Format run return 401?

The errors page lists the causes of unauthorized at create: no key, a malformed key, a revoked key, a key for the other host, or two credentials at once. All of them are the same HTTP status and the same code, so the status alone does not tell you which one you hit.

Because a 4xx at create means nothing ran and nothing was charged, a wrong header costs you nothing. The error envelope also gives you next_action: authenticate, which confirms the problem is the credential and not the payload.

Causes of 401 unauthorized on a Format create, read 2026-10-02
CauseWhat to check
No keyThe header is missing entirely
Malformed keyTruncated or quoted value, stray whitespace or newline
Revoked or unknown keyThe key was deleted or rotated in the dashboard
Key for the other hostA dev key sent to api.sume.com, or the reverse
Two credentials at onceBoth Authorization and x-api-key are present

How do I see which headers my client really sends?

Print the final request, not the one you meant to build. Many HTTP clients merge default headers from a session object, an environment-level setting or a gateway in front of your service, so the request that leaves the machine can differ from the dictionary in your code.

The simplest check is a verbose curl against the same URL. If curl works with one header and your application fails, diff the headers. Do not paste the key into logs or tickets while you do this; log header names only.

curl -sS -v -X POST "https://api.sume.com/v1/formats/acme/live-commerce/runs" \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8823-lc-v1" \
  -d '{"instruction":"Make one 9:16 clip."}' 2>&1 | grep -i '^> '

How do I send exactly one credential from code?

Pick one spelling and build the headers in one place. The sample below sets Authorization and refuses to build the request if an x-api-key header is also present, so a duplicate fails on your side before it reaches Sume. It uses only the Python standard library and does not send the request until you uncomment the last line.

The same single-credential rule applies to bulk queues and to reads, because they share the key rules of a single Format run.

import json, os, urllib.request

def build(handle, slug, body, extra=None):
    headers = {
        "Authorization": "Bearer " + os.environ["SUME_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": body.pop("_key"),
    }
    headers.update(extra or {})
    names = {h.lower() for h in headers}
    if {"authorization", "x-api-key"} <= names:
        raise ValueError("send one credential, not two")
    url = f"https://api.sume.com/v1/formats/{handle}/{slug}/runs"
    return urllib.request.Request(url, json.dumps(body).encode(), headers)

req = build("acme", "live-commerce", {"_key": "order-8823-lc-v1", "instruction": "One clip."})
print(req.full_url, sorted(req.headers))
# urllib.request.urlopen(req)  # uncomment to send

What if the single header still returns 401?

Work down the remaining causes. Confirm the key was minted for the host you call: staging uses api.dev.sume.com and production uses api.sume.com, and a key for one is refused by the other. Then check the key has not been revoked.

Do not confuse this with a 403. A key that authenticates but lacks formats:write gets 403 insufficient_scope, and a team Format called with a personal key gets 403 workspace_key_required. Those are covered in the 403 post and the team key post. A 401 means Sume could not establish who you are; a 403 means it did and said no.

What should I log so the next 401 is quick?

Log the response error.code, the x-sume-request-id header (also error.request_id), the host and the header names you sent. Quote the request id if you contact support. Never retry a 401 in a loop: nothing about the request changes between attempts, and the answer will be the same.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume