API reference/Affidavits

Report affidavits

confirm

Report times, outside-window airings and misses for a batch of units.

POSThttps://affiliates.radioworkflow.com/api/v1/affiliations/{id}/weeks/{week}/affidavits
Confirm: this records an affidavit, the proof behind the network's invoices.

Records what happened to a batch of ROS units: the time each aired, an airing outside its window, or a miss. The network's invoices list these reports under each unit as proof, and a change after an invoice is sent becomes an adjustment, so every item is checked:

  • aired: aired_date is the unit's day (a weekly unit: one of its allowed days whose window has closed) and aired_at is inside its window.
  • outside: aired on a day of the broadcast week but not inside the window on one of the unit's own days, with a reason of 1 to 300 characters.
  • not_aired: with a reason.
  • pending: clears an earlier report, until the week is signed.

A unit takes a time only once its window has closed. Send each unit's rev and the week's epoch from get-week unchanged. With all_or_nothing false (the default) the valid items are saved and errors lists the others with a code each; with true any refusal saves nothing and answers 422. At most 2,000 items per call, each unit once.

Authentication

Send a station key as Authorization: Bearer rwaff_k_.... A key with the report scope. Keys and scopes

Path parameters

idstring1 to 64 charactersrequired
The affiliation id from GET /v1/affiliations: one station's relationship with one network.
weekdaterequired
Monday of the broadcast week, YYYY-MM-DD. Any other day answers 400.

Request body (JSON)

epochintegerat least 0required
week.epoch from get-week, sent back unchanged. It moves when the network reopens the week, so a report built on an older read is refused with 409 epoch_changed.
all_or_nothingbooleanoptionaldefault false
true saves nothing when any item is refused (422 validation_failed). The default false saves the valid items and lists the others in errors.
itemsarray of objectsrequired
The units to report, 1 to 2,000, each once.
unit_idstring1 to 20 charactersrequired
The unit's id from get-week.
revintegerat least 0required
The unit's rev from get-week.
statusenumrequired
What happened.
airedoutsidenot_airedpending
aired_datedateoptional
The day it aired, YYYY-MM-DD. Required for aired and outside.
aired_attimeoptional
Minutes after midnight (0 to 1439) or HH:MM, in the station's time zone. Required for aired and outside.
reasonstring1 to 300 charactersoptional
Required for outside and not_aired.

Idempotency

Send a new Idempotency-Key header with every request (8 to 48 letters, digits, dashes or underscores; a UUID is a good choice). To retry a request that timed out, send it again with the same key: if the first attempt saved, the repeat answers its first result instead of recording twice. A reused key records nothing new: here it answers its first result even when the body changed, and after a request to another endpoint it usually fails with 502 upstream_failed. Never reuse one for a new request. How it works

Response

200application/jsonThe units saved, the items refused and the week's new state.

Response schema

savedAffiliateUnit[]required
errorsItemError[]required
weekobjectrequired
week.state"signed" | "ready_to_sign" | "in_progress" | "published"required
week.epochintegerrequired
week.revintegerrequired
AffiliateUnit27 fields
idstringrequired
datedaterequired
daypartstringrequired
windowStartinteger (0 to 1439)required
windowEndinteger (0 to 1439)required
creativeIdstringrequired
advertiserstringrequired
productstringrequired
titlestringrequired
iscistringrequired
lengthintegerrequired
cartstringrequired
status"pending" | "aired" | "outside" | "not_aired"required
airedAtinteger (0 to 1439)optional
airedDatedateoptional
reasonstringoptional
source"portal" | "log_upload" | "rw_log" | "network" | "api" | "mcp"optional
addedInVersionintegerrequired
removedInVersionintegeroptional
kind"dated" | "weekly"required
weekStartdaterequired
allowedDaysdate[]optional
revintegerrequired
copyStatus"ready" | "to_follow"required
instructionsstringoptional
rwState"confirmed" | "not_confirmed"optional
flagsstring[]required
ItemError6 fields
unit_idstringoptional
vehicle_idstringoptional
datedateoptional
error"rev_conflict" | "not_reportable" | "outside_window" | "invalid_time" | "invalid_date" | "reason_required" | "removed" | "not_found" | "break_not_found" | "local_break"required
messagestringrequired
currentAffiliateUnit or ShowAiringoptional
ShowAiring12 fields
keystringrequired
showIdstringrequired
datedaterequired
status"pending" | "carried" | "not_carried"required
startinteger (0 to 1439)optional
endinteger (0 to 1439)optional
uneditedbooleanoptional
dropped(object or object or object)[]optional
reasonstringoptional
source"portal" | "log_upload" | "rw_log" | "network" | "api" | "mcp"optional
airedDatedateoptional
revintegerrequired

Errors

400bad_requestThe request is not valid: a path value, a query value or a body field is missing, malformed or out of range.
403forbiddenThe key is valid but cannot do this for this affiliation: the affiliation is not one of the key's, or the person who made the key lacks the role.
403read_onlyThe affiliation is read-only: the network paused or ended it, or the station outlet is inactive. Its history stays readable.
403insufficient_scopeThe key does not carry the scope this endpoint needs (`read`, `report` or `acknowledge`).
409epoch_changedThe network reopened or changed the week after you read it, so the `epoch` you sent is out of date.
409week_signedThe week is signed, so its affidavits can no longer change.
413payload_too_largeThe request body is over the limit: 256 KB of JSON (512 KB when applying log matches), or 10 MB for an uploaded log.
415unsupported_media_typeThe body was not sent as JSON.
422validation_failedItems were refused. With `all_or_nothing` true nothing was saved; `errors` says why for each item.
500not_savedThe change could not be saved, so nothing changed.
502upstream_failedThe service behind the API answered in a way the portal could not use.
Possible on every endpoint
401invalid_principalThe key is missing, malformed, unknown, revoked or expired; all of these answer the same way.
429rate_limitedToo many requests: the key's bucket, a daily limit or the failed sign-in brake refused this one.
500server_errorSomething went wrong on our side. The answer never carries internal detail.
503unavailableThe service is not available right now: it could not be reached, or it is not set up yet.

Every code, its fix and when to retry

Rate limits

Failed sign-ins per address10 per minuteportal:auth
Burst per key60 per minuteportal:key
Calls per key600 per 10 minuteskey:{prefix}
Writes per key120 per minutew:{prefix}

The limits and the RateLimit headers

MCP

An assistant calls this as the tool report_affidavits, with the same input as one object. The MCP server
Request
# A new Idempotency-Key for every new request. Reuse it only to retry this same request.
IDEMPOTENCY_KEY=$(uuidgen)

curl -X POST 'https://affiliates.radioworkflow.com/api/v1/affiliations/srn-ksmt/weeks/2026-09-21/affidavits' \
  -H "Authorization: Bearer $AFFILIATE_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{
  "epoch": 1,
  "items": [
    {
      "unit_id": "u-01oso1x",
      "rev": 1,
      "status": "aired",
      "aired_date": "2026-09-22",
      "aired_at": "07:42"
    }
  ]
}'
Request body
{
  "epoch": 1,
  "items": [
    {
      "unit_id": "u-01oso1x",
      "rev": 1,
      "status": "aired",
      "aired_date": "2026-09-22",
      "aired_at": "07:42"
    }
  ]
}
Response200
{
  "saved": [
    {
      "id": "u-01oso1x",
      "date": "2026-09-22",
      "daypart": "ROS",
      "windowStart": 360,
      "windowEnd": 1139,
      "creativeId": "trhd",
      "advertiser": "Trailhead Outfitters",
      "product": "",
      "title": "Hunting Season Kickoff",
      "isci": "SR_TRHD0921060",
      "length": 60,
      "cart": "K90388",
      "status": "aired",
      "addedInVersion": 1,
      "kind": "dated",
      "weekStart": "2026-09-21",
      "rev": 2,
      "copyStatus": "ready",
      "flags": [],
      "airedAt": 462,
      "airedDate": "2026-09-22",
      "source": "api"
    }
  ],
  "errors": [],
  "week": {
    "state": "in_progress",
    "epoch": 1,
    "rev": 2
  }
}

Try it

Demo data, no key, nothing saved