API reference/Affidavits

Upload a log

write

Upload a log in any format; returns proposed matches and saves nothing.

POSThttps://affiliates.radioworkflow.com/api/v1/affiliations/{id}/weeks/{week}/log-uploads
Write: this stores the log and proposes matches. It saves no affidavit.

Send the as-run log your automation system saves, in whatever format it uses, and get back proposed matches between its lines and the week's units. Nothing is saved as an affidavit: review the matches and apply the ones you accept with apply-log-matches.

The file goes in the multipart field file: CSV, TSV, TXT, LOG, ASC, DAT, XLS, XLSX or PDF, at most 10 MB, one broadcast week per file.

The answer waits up to 60 seconds for the file to be read. A file read in time answers 200 with the matches, and a file that could not be read answers 422 upload_failed. A file still being read answers 202 with its id: poll GET /api/uploads/{upload} with the same station key every few seconds until status is no longer reading. The poll answers {"status": "ready", "result": {...}}, where result is the object a 200 carries in upload, or {"status": "failed", "message": "..."}. Polling uses no upload quota.

Each match has a group: matched lines can be applied as they are, check lines need a look, and missing units found no line. Uploads are limited per affiliation and per key, and are not idempotent: sending the same file again is a new upload.

The key needs the report scope on an affiliation that takes reports. Without it, and for a read-only affiliation, the answer is 403 forbidden before the file is read; read_only comes only from an affiliation that became read-only during the upload.

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.

Form fields (multipart/form-data)

filefilerequired
The log file, at most 10 MB: csv, tsv, txt, log, asc, dat, xls, xlsx or pdf.

Response

202application/jsonThe upload id while the file is still being read; 200 with the proposed matches when it was read within 60 seconds.

Response schema

The body is one of UploadCreated or UploadReady.

UploadCreated3 fields
uploadobjectrequired
upload.idstringrequired
upload.status"reading"required
UploadReady1 field
uploadUploadResultrequired
UploadResult15 fields
idstringrequired
fileNamestringrequired
linesReadintegerrequired
linesIgnoredintegerrequired
columnsstring[]required
matchesobject[]required
matches[].unitIdstringrequired
matches[].group"matched" | "check" | "missing"required
matches[].rowRefstringoptional
matches[].rawstringoptional
matches[].status"aired" | "outside"optional
matches[].airedDatedateoptional
matches[].airedAtinteger (0 to 1439)optional
matches[].confidencenumber (0 to 1)required
matches[].whystringrequired

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.
404not_foundNothing was found for the values in the path: no audio is ready for the week yet, the upload id is unknown, or the record does not exist.
409nothing_to_matchNo unit in this week is waiting for a time, so there is nothing a log could match.
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_fileThe uploaded file is not a log export the reader accepts.
422upload_failedThe log could not be read within the request. `upload_id` names the upload.
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}
Log uploads per affiliation20 per dayup:a:{affiliation id}
Log uploads per key10 per hourup:p:{prefix}
Log reading per network2,000,000 per dayllm:n:{network cloud}

The limits and the RateLimit headers

MCP

Not an MCP tool. Files go through the REST route or the portal; an MCP message cannot carry a 10 MB log.
Request
curl -X POST 'https://affiliates.radioworkflow.com/api/v1/affiliations/srn-ksmt/weeks/2026-09-21/log-uploads' \
  -H "Authorization: Bearer $AFFILIATE_API_KEY" \
  -F 'file=@asrun.csv' \
  --max-time 90
Response202
{
  "upload": {
    "id": "u1207_20260921_4f1c2a9e8b7d4c3fa0e1d2c3b4a59687",
    "status": "reading"
  }
}

Try it

Demo data, no key, nothing saved

A file cannot be sent from the docs. The documented answer is shown instead.