Perplexity Decisions API as a publish gate for Sume output
Check a finished Sume Format image with Perplexity's Decisions API before it ships: base64 data URL, one yes/no question, a threshold, and a human-review lane.

You can put a decision model after a Sume Format run: when the run finishes, your receiver downloads the image from primary_output_url, sends it to Perplexity's Decisions API with one yes/no question, and publishes only if the returned probability clears a threshold you choose. Sume does not call Perplexity for you; this is a step in your own code, after the webhook.
The check is independent of the run, which is the point. A Format's output_schema can make the generating agent report a boolean about its own work, but an agent grading itself is weaker evidence than a separate model looking at the file.
What does the Decisions API take and return?
Perplexity's quickstart describes a single endpoint, POST https://api.perplexity.ai/v1/decisions, with Bearer auth and the model pplx-decider-v1-27b. You pass a state and a set of named questions; a noul question answers with a probability of yes between 0 and 1.
| Fact | Value | What it means for a Sume gate |
|---|---|---|
| Image input | PNG, JPEG or WebP as base64 data URLs only | Download the artifact first; an https link is not accepted |
| Questions per request | 1 to 128 | Ask several checks in one call |
| Question types | noul, choice, score | noul is the yes/no gate |
| Rate limit | 10 requests per second per organization | Fine for a webhook receiver; throttle a backfill |
| Price | $0.04 per million input tokens, output free | Verify on the page before you budget |
What does the gate look like in code?
This takes the receipt's primary_output_url, which for an image Format is a durable media.sume.com URL, and returns whether to publish. It refuses to guess when the file type is not one the API lists.
import base64, os, requests
OK_TYPES = {"image/png", "image/jpeg", "image/webp"}
def gate(image_url, question, threshold=0.9):
img = requests.get(image_url, timeout=60)
img.raise_for_status()
kind = img.headers.get("content-type", "").split(";")[0]
if kind not in OK_TYPES:
return "review"
data_url = f"data:{kind};base64," + base64.b64encode(img.content).decode()
body = {
"model": "pplx-decider-v1-27b",
"state": [{"type": "image_url", "image_url": {"url": data_url}}],
"questions": {"ok": {"type": "noul", "instructions": question, "criteria": "Answer yes only if it is clearly true."}},
}
r = requests.post(
"https://api.perplexity.ai/v1/decisions",
json=body,
headers={"Authorization": "Bearer " + os.environ["PERPLEXITY_API_KEY"]},
timeout=60,
)
if r.status_code != 200:
return "review"
p = r.json()["answers"]["ok"]["noul"]
return "publish" if p >= threshold else "review"Where does it sit in the webhook flow?
The gate belongs in the worker that handles the webhook, after the delivery has been acknowledged.
- Verify the signature and answer 2xx first; Sume gives a receiver 10 seconds, so queue the gate instead of running it inline.
- Fetch the receipt if
payloadis null, which happens when the delivery would exceed 1 MiB;error.result_urlpoints to it. - Branch on
outcome. Onlyokhas a usableoutput; ondegradedthere is media but no structured output, so send it straight to review. - Run
gate()onprimary_output_url. Apublishgoes out; anything else goes to a person with the receipt id.
What does this not cover?
The quickstart lists images, not video, so a clip needs a still frame that you extract yourself, for example with ffmpeg, before the call. A single frame cannot vouch for the other 29 seconds. Treat the check as a cheap filter for obvious misses such as a missing logo or the wrong product, not as approval of the whole video.
Every failure path in the function returns review, never publish. A 429 or a 504 from the decision API, a wrong file type and a network error all leave the asset waiting for a person, which is the safe default for an automated pipeline. Pick the threshold by running the gate over assets you have already judged by eye, and keep each question about one visible fact, such as whether the logo appears.
Sources
Related posts
More in Developers
- Pocket TTS API: run it yourself or call a hosted TTS API
Kyutai's Pocket TTS installs with pip and serves from localhost. If you want a hosted API with job URLs instead, here is the Sume request and what changes.
- Prefect 3 task retries for a Sume job: same Idempotency-Key
Retry a Sume image job in Prefect 3 without paying twice: a tested flow with retry_condition_fn, delay list and an order-derived idempotency key.
- Pydantic model to Sume output_schema: extra forbid, no defaults
Turn a Pydantic v2 model into a valid Sume Format output_schema: extra=forbid, nullable instead of defaults, and the SumeMediaFile reference. Tested.
- Unit test Sume webhook signature checks in pytest (Python)
A pytest file for HMAC webhook verification: tamper, rotation header, stale timestamp and empty secret, written against Sume's sume-v1 scheme.
Written by Sume