Skip to main content

Submitting Render Jobs

A render job tells dinkem.gg which moment to turn into a clip. Most integrations only ever need automatic clipping: send a demo, a player and a round, and we find the moments for you.

  • Automatic clipping (round mode) — provide a demo and a player to follow, optionally scoped to a single round. We parse the demo, find that player's kills and stitch them into one clip. No tick math on your side. This is the path to start with.
  • Timeline mode — the advanced escape hatch: provide an array of tick-range segments, each with its own player POV, when you need to control exactly what gets recorded.

You submit a job with a single POST request; we queue it and render the clip asynchronously.


Endpoint

POST https://dinkem.gg/api/clips
MethodPOST
AuthAuthorization: Bearer YOUR_API_KEY (see Authentication)
Content-Typeapplication/json

The key you authenticate with selects the job type: your metered key submits best-effort renders (metered per rendered hour), your reserved key submits priority renders covered by your provisioned dinks. The request body is identical for both.


Request body

The body is a JSON object. You must provide either steam64_id (automatic clipping, optionally with round) or timeline (timeline mode) — they are mutually exclusive.

Common fields

FieldTypeRequiredDescription
demo_urlstringyesA publicly reachable URL to the CS2 demo (.dem) file to render.
fpsintegernoRecording framerate. Any integer from 5 to 120. Defaults to 30 when omitted.
resolutionintegernoRender resolution. Either 1080 or 1440. Defaults to 1080 when omitted. Together with fps it determines the metered rate.
commandsobjectnoPer-job CS2 console command overrides. Each key is a command name, each value is a string. See Supported commands below.
hud_killfeedbooleannoWhen true, replaces CS2's built-in killfeed with dinkem's own — it persists across stitched clips and shows only the POV player's kills. Defaults to false. See HUD Modifications.
hud_damage_tickerbooleannoWhen true, overlays damage numbers near the crosshair whenever the POV player deals damage. Defaults to false. See HUD Modifications.
thumbnail_stylestringnoWhich cover-art thumbnail to generate for the clip. full (the default) shows the hero kills label (4K, ACE) and {player} - Round {n} subtitle over a gradient alongside the killfeed; killfeed shows only the killfeed on the bare frame; none draws no cover art, so the thumbnail is a plain frame from the clip. See HUD Modifications.
watermark_image_idstringnoThe UUID of a watermark you uploaded on the dashboard Assets page (see Managing Assets). When set, that image is composited onto the top-left of the rendered clip. The image is treated as 1:1 at 1080p and scaled automatically for other resolutions. Must be a watermark you own — an outro's ID is not accepted here.
idempotency_keystringnoAn arbitrary string (max 255 characters) identifying this logical request, scoped to your account. See Idempotency below.
forcebooleannoOnly meaningful alongside idempotency_key. Requests a redo of a job that already finished. See Idempotency below.
extrasany JSON valuenoOpaque passthrough data. We don't inspect or validate it — whatever you send is stored as-is and echoed back unmodified (same JSON types, no stringification) in the extras field of the outcome webhook, on both success and failure. Use it to correlate a clip back to your own record.
webhook_urlstringnoWhere to deliver this job's outcome webhook — sent when the job finishes, whether it produced a clip or failed. Must be an http(s) URL. Configured per request, so you can route jobs to different endpoints for different purposes. When omitted, no webhook is sent for the job.
webhook_auth_headerstringnoAuthorization header value to send with this job's webhook, forwarded verbatim. Only applies alongside webhook_url — sending it without webhook_url returns a 400.

Round mode fields

FieldTypeRequiredDescription
steam64_idstringyesThe 64-bit Steam ID of the player whose point of view should be captured.
roundintegernoThe round number to capture. Must be a positive integer (1 or greater). When omitted, all kills by the player in the demo are included.
pre_kill_paddingnumbernoSeconds of lead-in captured before each kill. Any number from 0.5 to 5. Defaults to 3. Round mode only.
post_kill_paddingnumbernoSeconds of lead-out captured after each kill. Any number from 0.5 to 5. Defaults to 2. Round mode only.

Timeline mode fields

FieldTypeRequiredDescription
timelinearrayyesAn array of 1–20 segment objects defining exactly what to record.

Each segment in the timeline array is an object with:

FieldTypeDescription
steam64_idstringThe 64-bit Steam ID of the player whose POV to capture for this segment.
start_tickintegerThe demo tick to start recording. Must be a non-negative integer.
end_tickintegerThe demo tick to stop recording. Must be greater than start_tick.

Segment constraints:

  • Each segment must be at least 64 ticks long (~1 second at 64-tick demos).
  • Each segment must be at most 64,000 ticks long (~16 minutes).
  • Maximum 20 segments per job.
  • Adjacent/overlapping segments with the same player POV are merged automatically.

Automatic clipping example

Three fields is a complete request — every other field has a default:

{
"demo_url": "https://cdn.example.com/matches/abc123.dem",
"steam64_id": "76561198000000000",
"round": 5
}

That renders every kill the player got in round 5 as a single 1080p30 MP4.

Add the optional fields when you need them:

{
"demo_url": "https://cdn.example.com/matches/abc123.dem",
"steam64_id": "76561198000000000",
"round": 5,
"fps": 30,
"resolution": 1080,
"pre_kill_padding": 3,
"post_kill_padding": 2,
"commands": {
"spec_show_xray": "1"
}
}

How automatic clipping picks the moments

You don't tell us where the action is — we work it out from the demo:

  • Every kill by steam64_id in the round is captured, from roughly 3 seconds before the kill to 2 seconds after. Adjust that window with pre_kill_padding / post_kill_padding (in seconds) when you want a longer run-up or a shorter tail.
  • Kills close enough together that their clips overlap are merged into one continuous shot, rather than repeating the same seconds twice.
  • When the last kill of a round ends it, the clip is extended over the win announcement so it doesn't cut off mid-callout.
  • Everything is stitched into a single MP4 — one job, one clip, however many kills it contains.

Omit round and the same thing happens across the whole demo: every kill by that player in the match, in one clip.

Timeline mode example

Record a player throwing a flashbang, then show the POVs of two flashed enemies:

{
"demo_url": "https://cdn.example.com/matches/abc123.dem",
"timeline": [
{ "steam64_id": "76561198000000001", "start_tick": 12000, "end_tick": 12256 },
{ "steam64_id": "76561198000000002", "start_tick": 12200, "end_tick": 12500 },
{ "steam64_id": "76561198000000003", "start_tick": 12200, "end_tick": 12500 }
],
"fps": 60
}

Supported commands

The commands object accepts the following keys:

CommandValuesDefaultDescription
spec_show_xray"0", "1""0"Show player outlines through walls.
cl_draw_only_deathnotices"0", "1""1"When "1", hides the HUD except for kill feed. Set to "0" to show the full HUD.

Only the commands listed above are accepted. Unknown command names or invalid values return a 400.

Field types are validated strictly

demo_url and steam64_id must be JSON strings, round (when present) must be a JSON integer, and timeline must be an array of objects. Sending a number where a string is expected (or vice versa), or a non-integer round, returns a 400. Wrap steam64_id in quotes — a bare 17-digit number can lose precision in some JSON encoders. When fps is present it must be an integer from 5 to 120, and when resolution is present it must be exactly 1080 or 1440. When present, idempotency_key must be a non-empty string of at most 255 characters. When present, watermark_image_id must be a valid UUID string identifying a watermark your account uploaded on the Assets page — an unknown, unowned id, or the id of an asset that isn't a watermark (an outro, say) all return a 400. When present, thumbnail_style must be exactly full, killfeed, or none; any other value returns a 400. When present, pre_kill_padding and post_kill_padding must each be a number from 0.5 to 5 seconds (fractional values allowed) and apply only in round mode — sending either alongside timeline returns a 400.


Idempotency

Pass idempotency_key (any string you choose) to make a submission safe to retry. It's scoped to your account — the same key on two different accounts does not collide.

  • Resubmitting the same idempotency_key (without force) does not queue a new job. It returns HTTP 200 with the job_id of the original request, unchanged. This is the mechanism for avoiding duplicate jobs from client-side retries.
  • Resubmitting with force: true requests a redo:
    • If the original job is still queued or rendering, this is a no-op — you get back the existing job unchanged (200), so a redo never races an in-progress render.
    • If the original job already finished (successfully or with an error), it's re-queued in place using the same job_id and the payload from this request. Once it renders, it overwrites the original clip rather than producing a second one.
  • force: true without idempotency_key returns a 400 — there's nothing to redo without a key to match against.
  • A never-before-seen idempotency_key always queues a new job (201), whether or not force is set.

Response

On success the API returns the ID of the job as { "job_id": "<uuid>" }:

{ "job_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d" }
  • 201 — a new job was queued (either you didn't send idempotency_key, or it's the first time this account has used that key).
  • 200 — no new job was queued. This means idempotency_key matched an existing job: either it was replayed as-is, or (with force: true) it was re-queued in place for a redo. See Idempotency above.

The job_id is a UUID identifying your render job. Store it so you can correlate the job with the clip it produces.

Submitting a job only queues it — rendering happens asynchronously. A 2xx response means the job was accepted, not that the clip is ready. Pass a webhook_url (see Webhooks) to be notified once rendering finishes, rather than polling. You'll be notified either way: a job that fails permanently sends a job.failed webhook carrying an error_code.

You can also look a job up by its job_id at any time to check its status, read its error_code, or find the clip it produced — and re-run it — via the job endpoints.


  1. Resolve the demo URL and the player's steam64_id, plus the round you care about — that's all automatic clipping needs. Only if you need frame-exact control, determine the tick ranges and player POVs for timeline mode instead.
  2. POST /api/clips with your chosen mode and API key.
  3. Store the returned job_id against the match/player in your own system.
  4. Surface the finished clip to your users — embed it via iframe or download the MP4 — once you're notified via the outcome webhook, and handle job.failed for the jobs that don't make it.