The error body
Every error is JSON with amessage and a code. Validation errors (422) add errors, every
problem at once:
{
"message": "The colour field is not supported.",
"code": "validation_failed",
"errors": { "colour": ["The colour field is not supported."] }
}
codeis for your program: one per situation, and it does not change. Branch on it, never on the wording ofmessage.messageis for people: show or log it. It is a string or, on some statuses, an array of one string: read it asArray.isArray(m) ? m[0] : m.errorsmaps a field to its messages. Outsideconfigthe key is the field name (name,status,parts.0.etag). Insideconfigit is a JSON pointer into your request:/config/translations/0/languageis thelanguageof the first translation.
Accept: application/json to be sure an error comes back as JSON.
Error codes
code | Status | Meaning | What to do |
|---|---|---|---|
bad_request | 400 | The request cannot be acted on as sent. | Read message. |
invalid_output_path | 400 | path is not a file under output/. | Use a path from GET …/outputs. |
invalid_idempotency_key | 400 | Idempotency-Key is not 1 to 255 visible ASCII characters. | Use a UUID. Repeating a create. |
account_required | 400 | SyncWords staff only: the account to act for is not named. | — |
unauthenticated | 401 | No token, or an expired or unknown one. | Create a new token and repeat the call. |
invalid_credentials | 401 | POST /token/create with a key pair that matches no account. | Check the pair; repeating it does not help. |
staff_only | 401 | A call only SyncWords staff may make. | — |
forbidden | 403 | The token may not make this call. | — |
vod_express_not_enabled | 403 | VOD Express is not available to the account. | Ask support. |
account_restricted | 403 | The account is restricted: its access was revoked, or its trial expired or ran out. message says which. | Ask support. |
not_found | 404 | No such route or resource. | Check the path. |
project_not_found | 404 | No such project in your account. | Check the id, and the token’s account. |
file_not_found | 404 | No input file of that name. | Check the name and its encoding. |
file_meta_not_found | 404 | Nothing recorded about the file yet. | Wait: processing has not reached it. |
output_not_found | 404 | No such output. | It may not be written yet: see available in GET …/files. |
feed_not_found | 404 | No such output feed or feed media file: the project publishes no feed in that language, or the feed has not been written yet. | Check the MrssOutput languages in the config; a new feed appears once the source feed has been read. Media RSS feeds. |
upload_not_found | 404 | The upload was completed, aborted, expired, or never started. | Start a new upload. |
webhook_not_found | 404 | No such webhook in your account. | Check the id: GET /webhooks. |
webhook_delivery_not_found | 404 | The webhook has no such delivery. | Check the event id: GET /webhooks/{id}/deliveries; deliveries are kept 7 days. |
aws_access_not_provisioned | 404 | No upload role yet. | PUT /account/aws-access. |
storage_connection_not_found | 404 | No such storage connection in your account. | Check the id: GET /account/storage-connections. |
method_not_allowed | 405 | The path exists, but not with this method. | Check the method. |
conflict | 409 | The request conflicts with the resource’s current state. | Read message. |
file_exists | 409 | An API upload of a name already in input/, without overwrite. | Set overwrite: true, or choose another name. |
project_reads_feed | 409 | An upload to a project whose sourceMedia is a feed: the feed is its only source. | Add the media to the feed, or create a folder project. |
project_reads_folder | 409 | GET …/source-feed of a project that reads its input folder: it has no source feed. | — |
file_processing | 409 | Replacing or deleting a file that the running project may be processing: it is neither done nor failed yet. | Wait until it is, or stop the project, then repeat the call. |
project_config_locked | 409 | SyncWords support manages the project’s configuration for now (meta.configLocked is true): the call would change config or buildVersion. name and description can still be changed. | Contact support. |
idempotency_in_progress | 409 | The first POST /projects with this Idempotency-Key has not finished. | Repeat the call after Retry-After seconds. |
storage_connection_in_use | 409 | Deleting a storage connection a project of your account uses. | Move the project to other storage (PATCH /projects/{id} with storage), then delete. |
payload_too_large | 413 | The body is larger than 2 MB. | Send less: a config, or up to 10,000 parts, fits. Limits. |
unsupported_media_type | 415 | The body is neither JSON nor YAML. | Set Content-Type: application/json (or application/yaml). |
validation_failed | 422 | The request is invalid. | Fix every field in errors. |
service_start_failed | 422 | The config is valid, but the processing service did not start. | POST /projects/{id}/start; the project exists. |
idempotency_key_reused | 422 | The Idempotency-Key was used with another body. | Use a new key for a new project. |
webhook_limit_reached | 422 | The account has 10 webhooks, the most it may have. | Delete one first. |
storage_failed | 422 | The project’s folders could not be created; nothing was stored. On your own bucket: the keys could not write to it. | Repeat the call; on your own bucket, check the key’s permissions. |
storage_connection_check_failed | 422 | The bucket could not be listed with the keys sent: a wrong key, bucket or endpoint, or no permission. message carries the store’s own words. | Fix the keys, bucket or endpoint. Your own bucket. |
storage_connection_folder_only | 422 | A project on your own bucket with a feed source or a feed output. | Use SyncWords’ storage for feed projects, or folders on your bucket. |
storage_prefix_taken | 422 | Another project of your account watches this input folder, or one inside it, on the same connection. | Choose another inputPrefix. |
build_without_storage_connections | 422 | The build named cannot read your own bucket. | Choose a later build from GET /build-versions. |
build_has_no_schema | 422 | The named build cannot be used. | Choose another from GET /build-versions. |
too_many_requests | 429 | More than 1000 requests in a minute from your account, 10,000 parts signed, or 60 refusals of one token or key pair. | Wait Retry-After seconds. Limits. |
internal_error | 500 | An unexpected error on our side. | Repeat once; then contact support. |
bad_gateway | 502 | A service the API depends on failed. | Repeat the call. |
service_stop_failed | 502 | A running service could not be stopped; nothing was changed. | Repeat the call. |
aws_access_failed | 502 | AWS refused a step of creating the upload role. | Repeat the call. |
platform_unavailable | 503 | The processing platform could not be reached; nothing was changed. | Repeat the call. |
service_unavailable | 503 | The API cannot serve this call right now. | Repeat later. |
build_list_unavailable | 503 | The list of builds, or a build’s schema, cannot be fetched right now; nothing was changed. | Repeat the call. |
Config validation
Two kinds of message come back under/config/…: the schema’s, about shape, and the API’s, about
meaning.
Schema messages
| Message | Example cause |
|---|---|
The ext field is required. | /config/outputs/0/items/0/ext is missing. |
The property model is not supported here. | A field this kind of object does not have: a typo, or an option of another engine (model on DeepL). |
Invalid value. Allowed values: keep, remove. | A value outside the allowed list (filterProfanity: "hide"), or an unknown @type (engine: {"@type": "ChatGPT"} lists the translation engines). |
The value must be of type array. | A wrong type (translations: "es"). A field that also takes null says string or null. |
The value must be less than or equal to 2. | Out of range (ttsOptions.volume: 3). |
Rule messages
These are checked once the config matches the schema, so fixing schema errors may bring up rule errors on the next attempt.| Message | Fix |
|---|---|
Language 'xx' is not supported by engine 'DeepL' | Choose another engine or language: GET /enums/languages. |
The dub track language must be a dubbed translation: es, ru. | A dub track (/config/outputs/0/items/0/dubTracks/1/language) names a language without ttsOptions. Dub it, or use a subtitle output. |
No translation has ttsOptions, so there is no dub to add: use useOriginalTracks for the input’s audio. | As above, with no dub at all. For the input’s own audio, set useOriginalTracks: true. |
An audio file needs its audio track: one dub track, or useOriginalTracks for the input’s audio. | An AudioFile with neither, under …/dubTracks. Add one dub track or useOriginalTracks: true. |
An audio file holds exactly one audio track: the input’s audio (useOriginalTracks) or one dub track, not both. | Keep one of them, and write the other into a second file. |
An audio file holds exactly one audio track: list one dub track. | Write one audio file per dub. |
The input’s audio is copied as it is, which an mp3 file cannot hold: use ipod (m4a) or matroska, or a dub track. (also a wav file) | Under …/useOriginalTracks. Use containerFormat: "ipod" with ext: "m4a", or matroska. |
The name template must contain ${name}, or every input would write the same file. | Add ${name} to nameTemplate. |
The name template uses ${lang}, which a VideoFile does not have: ${ext}, ${name}. | Only SubtitleFile has ${lang}. |
The language must be the transcription language or a translation language: en, es. | A subtitle file, or a video’s subtitle track (…/subTracks/0/language), in a language that is not produced. Add the translation. |
The ext must not be blank. (also format, and The name template must not be blank.) | Fill it in. A blank SubtitleFile template is taken as the default instead. |
The name template must render a relative path: … | The name must stay inside the project’s output/ folder: no leading /, //, . or .. segment, backslash or control character. |
The options must be an object. | Caption options are an object of renderer settings. |
The format is set on the entry itself: leave options.format out, or make it SRT. | Remove format from options; set the entry’s format. |
The dubs es and es-MX are the same language (es)… | A project dubs each language once: dub one of them, and keep the other as a subtitle translation if needed. |
The name template renders the same file name as outputs/0/items/0 (x.es.mp4). | Two items of one destination write the same file. Give one of them another template. |
A project has one output folder, which outputs/0 is already: list these items there. | Move the items into the first S3FolderOutput. |
An output needs at least one item. | Add an item to the destination’s items, or remove the destination. |
A feed output re-publishes the source feed: an MrssOutput needs an MrssSource as the sourceMedia. (also an RssOutput needs an RssSource) | A feed output in a folder project, or of the other kind than the source. Use an S3FolderOutput, or the output of the source’s kind. Feeds. |
A feed output needs exactly one video or audio file: the one each feed item links to. | Keep one VideoFile or AudioFile in the feed output’s items. |
outputs/1 is already the es feed. | One feed output per language. |
A project’s source cannot change between a folder and a feed: create another project. | A PUT/PATCH whose sourceMedia is the other type, under /config/sourceMedia/@type. Create a new project for the other source. |
The URL must be an https:// address. (also …must not contain a user name or password., …does not resolve to a public address, so the feed cannot be fetched.) | The sourceMedia feed URL, under /config/sourceMedia/url. |
Upload errors
code | Status | Message (in errors.name or errors.parts on a 422) |
|---|---|---|
validation_failed | 422 | The name must end in one of: .mp4, .mov, … |
validation_failed | 422 | The name must be a file name, or a path of folders and a file name separated by /. |
validation_failed | 422 | The name must not contain "${". Output names are made by putting the name into ${name}, and a ${ left over fails the file. |
validation_failed | 422 | x.mp4 is already in the project and its output x.es.mp4 would be overwritten by this file's. Rename the file. |
file_exists | 409 | A file with this name is already in the project. Send overwrite: true to replace it. |
file_processing | 409 | The file may be being processed. Wait until it is done or failed, or stop the project, then try again. |
project_reads_feed | 409 | This project reads its media from a feed; files cannot be uploaded to it. |
upload_not_found | 404 | Upload not found.: the upload was completed, aborted or expired. |
Retrying safely
| Call | Safe to repeat? |
|---|---|
Every GET | Yes |
POST /projects/{id}/start, …/stop, …/retry | Yes: they converge on the same state. |
PUT / PATCH /projects/{id} | Yes, with the same body. |
DELETE of a project, file or upload | Yes; a second call answers 404 (or 204 for an upload). |
POST /projects | With an Idempotency-Key: a repeat answers what the first call did. Without one, each call creates a project. Repeating a create. |
POST …/uploads/{uploadId}/complete | Yes, with the same body: once the file is in input/, a repeat answers 200 again. |
POST …/uploads | Starts a new upload each time; abort the ones you do not complete. An upload you never complete creates nothing and is discarded after 3 days. A PUT to its url is repeatable: the last one wins. |
Repeating a create
Send anIdempotency-Key header with POST /projects: any string of 1 to 255 visible ASCII
characters, unique per project you mean to create. A UUID is a good one. If the call times out or
the connection drops, repeat it with the same key and the same body:
KEY=$(uuidgen)
curl -s -X POST "$API/projects" -H "$AUTH" -H "Idempotency-Key: $KEY" \
-H 'Content-Type: application/json' -d @project.json
# … no answer? Repeat the very same call:
curl -s -i -X POST "$API/projects" -H "$AUTH" -H "Idempotency-Key: $KEY" \
-H 'Content-Type: application/json' -d @project.json
| The repeat finds | It answers |
|---|---|
| The first call finished | The first call’s answer, status included, with Idempotency-Replayed: true. No second project. The project is shown as it was then; GET /projects/{id} reads it now. |
| The first call still running | 409, code: "idempotency_in_progress", and Retry-After. Repeat the call again after that many seconds. |
| The key used with another body | 422, code: "idempotency_key_reused". |
| The first call created nothing | As if the key were new: the call runs. |
201, and a 422 with
code: "service_start_failed" (the project exists; start it with POST /projects/{id}/start). A
call refused before anything was created (validation_failed, build_has_no_schema, …) and a
5xx leave the key unused, so you can correct the body and send it with the same key.
Keys belong to your account, and are kept for 24 hours from their first use. The body is
compared by content: the same project sent as JSON or YAML, or with its fields in another order,
is the same request.
Finding a project by name
AnIdempotency-Key protects one create. To keep one project per configuration across runs, days
apart, look the name up before creating it:
# Create a project only if none has this name yet
NAME='Fishing Channel S01'
ID=$(curl -s -G -H "$AUTH" "$API/projects" --data-urlencode "name=$NAME" | jq -r '.data[0].id // empty')
[ -z "$ID" ] && ID=$(curl -s -X POST "$API/projects" -H "$AUTH" -H 'Content-Type: application/json' \
-d "$(jq -n --arg name "$NAME" --slurpfile c config.json '{name: $name, config: $c[0]}')" | jq -r .data.id)