The Stream Developers screen is the single place where you mint the credentials that talk to ClutchCall Stream, generate the keys that sign playback tokens and webhook deliveries, register webhook endpoints, and inspect what has been delivered. Everything the screen renders comes from four list procedures, all scoped to the current org: The first three load once per visit. The deliveries panel requests the 25 most recent rows and refreshes every 30 seconds while the screen is open.

API keys and scopes

A stream API key is a bearer token that your server presents to the control plane. Each key row carries: scopes is a list, not a single value, and the console renders one tag per entry. Scope is enforced per procedure: the control plane rejects a call when the presented key does not carry the scope that the called procedure requires. Mint one key per job and give it only the scopes that job needs — a key scoped to what it actually does limits the blast radius when it leaks. Scopes are fixed at creation time. There is no edit control on this screen: to change what a key can do, create a replacement key with the scopes you want, move your callers onto it, then revoke the old one.

Live and test environments

Every key belongs to exactly one environment, and the env tag on the row tells you which. The console colours live green and the test environment amber, because the two are the mistake that costs the most: a test key in production stops working, and a live key in a test harness touches real inputs, real assets and real billable delivery. The environments are separate credential namespaces. A key is not promoted from test to live — you mint a second key in the environment you want. The rest of the screen (signing keys, webhook endpoints, deliveries) is listed for the environment that the Stream shell is currently showing.

Key creation, preview-only storage, and rotation

Create API key mints a key and returns the full token string exactly once, at creation. After that the control plane stores only a hash of the token plus a short leading fragment (a dozen or so characters) in token_preview. That is why the key table shows the preview followed by mask characters and offers neither a reveal nor a copy control:
  • There is nothing to reveal. The cleartext is not retained anywhere, so no control could produce it.
  • There is nothing useful to copy. The preview is a fragment that can never authenticate, and putting it on the clipboard only invites pasting it somewhere as though it were a token.
The preview exists so that you can match a row in this table against a token you already hold in a secret store or a log line, and so that revoking the right key is unambiguous. Rotation is create-then-revoke. A lost token is not recoverable: create a new key, deploy it, confirm the new key’s last_used_at is moving, then use Revoke on the old row. Revoking is the only destructive action on a key; the row stays in the list afterwards and renders as revoked instead of offering a revoke control, so the history of which key was retired and when is preserved.

Signing keys and their two uses

Signing keys are asymmetric keys held by the control plane. They are not bearer credentials and are never presented as one. Each key exists for exactly one use, and the console labels the row accordingly: Each row shows the key id and its algorithm (alg) in mono type, plus the creation date. You reference the key id when minting a playback token (see below), and you use the matching public key when verifying a webhook delivery. Keep the two uses on separate keys. They have different exposure: a playback key signs tokens that go out to browsers many times a second, while a webhook key signs traffic that only ever goes to endpoints you control. Retiring one should not invalidate the other.

Signing key lifecycle states

status drives the status indicator on each row: The Retire control appears only on rows whose status is active — a rotating or retired key has nothing left to retire. Because retiring is offered only from active, the safe order is: generate the successor with Generate signing key, point your minting code at the new key id, and retire the old key once nothing still references it. For playback keys, “nothing still references it” means every token that the old key signed has passed its expiresIn. For webhook keys, it means your verifier has been taught the new public key.

Minting a signed playback token

Signed playback tokens are minted on your server, with the SDK, against a signing key whose use is playback:
signPlayback returns the token string. Append it to the playback URL as ?token=…; the player reads it from the URL and presents it on playback without further wiring. Never run this in the browser. The signing key is what makes a token valid, and minting is a server-side operation for the same reason the API key is: whatever can mint tokens can mint them for any viewer and any lifetime.

Webhook endpoints and event types

Add endpoint registers a URL that ClutchCall POSTs events to. Each endpoint row shows: An endpoint receives only the event types it is subscribed to, so the count column is the quickest check when an event you expected never arrived: a healthy endpoint subscribed to the wrong set looks perfectly fine. Every delivery is signed with the signing key whose use is webhook signature. Verify that signature on your endpoint before you act on the payload, using the algorithm shown on the signing key row — an unsigned or unverified POST to a public URL is an open write endpoint. While a webhook key is rotating, accept signatures from both the rotating key and its active successor, then drop the old one once you retire it. Delete removes an endpoint. Deleting stops future deliveries to that URL; it does not remove the delivery rows already logged for it.

Webhook endpoint health states

The console collapses status and consecutive_failures into one indicator: An active endpoint with a non-zero failure count shows as failing, not as healthy — a key detail, because the endpoint is still enabled and still accruing attempts. Read last_status_code next: a 4xx usually means the payload or the signature check is being rejected, while a 5xx or a missing code points at the endpoint itself being down. A successful delivery resets the counter and the row returns to Healthy.

Delivery states, retries, and resending

The Recent event deliveries table is the delivery log — the 25 most recent attempts, refreshed every 30 seconds. There is no separate log explorer, so this panel is where a delivery question gets answered. Each row carries the event_type, the obj_id of the object the event is about (a live input, an asset, and so on), the response, and the timestamp. The response column has three renderings, and the distinction matters: Queued is not failed. A delivery that has been accepted for delivery but has no response yet renders neutral rather than red, so a burst of in-flight events does not read as an outage. Only a delivery that has actually come back non-2xx, or that is marked failed, is an error. Deliveries that fail are retried automatically with backoff — successive attempts are spaced further apart rather than hammering a dead endpoint — and each failed attempt increments the endpoint’s consecutive_failures, which is what eventually moves the endpoint row out of Healthy. Resend on a delivery row asks for that event to be delivered again. It queues a fresh attempt; it does not cancel, replace, or reprioritise an attempt that is already queued or in flight. Resending a delivery that is still showing — Queued therefore leaves you with two attempts for the same event, so wait for a response before resending. Because a resend is an additional delivery of the same event, your endpoint handlers should be idempotent on the event id.
  • Authentication — API keys, relay tokens, and signing-key rotation across the platform
  • Telemetry — operational data streams and where to scrape them