Errors

An error answers with an HTTP status and a JSON body that names a stable code:

{"error": "invalid", "details": {"name": ["has already been taken"]}}

error is always there; read it, not the wording. details appears only for some codes. Its messages are written for people: show them to the admin, do not parse them.

Every code

Status error Meaning What to do
400 bad_request The body is not valid JSON, or a required parameter is missing (then details.missing names it). Fix the request. Send Content-Type: application/json with a valid, flat JSON body.
401 invalid_token The key is missing, mistyped, expired or revoked, or its admin is no longer an owner or admin of the community. The answer is the same for every cause, with the header WWW-Authenticate: Bearer realm="Zen Social", error="invalid_token". Check the Authorization: Bearer … header. If it is right, stop and ask the admin for a new key. See Keys and access.
403 forbidden The key works, but its admin could not do this in the app, or (for /conversations) the key was not made with the permission May import direct messages. For example change a circle they are not an owner or admin of, post in a circle they are not a member of or may only read or comment in, comment in a circle they may only read, change a comment someone else wrote in the app, or read a post or event they could not see. Ask the admin to join that circle in the app, with a role that may post there, or that may comment there for a comment (as its owner or admin, if it is to be changed). Do not look for a way around it.
404 not_found It does not exist, it belongs to another community, or it is a deleted post or event. Also an event whose date is still being voted on (a "find a time" poll), a comment on a post or event the admin could not see, and a comment on a newsletter edition. A comment that has been deleted answers this to a PATCH and to a second DELETE, though GET still shows it as a deleted comment. For a claim: this community never imported a post, event or comment with that author external_id. For an undo of a conversation: this community never moved one in with that external_id, or it has been undone already. Check the id. A key never reaches outside its community.
409 record_deleted A create's external_id belongs to something that was imported and has been deleted since (or turned into an event in the app, for a post, or into a post, for an event; for a comment, its post or event was deleted). It is never brought back. Skip it and report it to the admin. Do not delete or retry.
409 changed_since_import A create's external_id belongs to something that someone changed in the app after your last write. It was not overwritten. Skip it and report it to the admin. Do not delete or retry.
409 in_edition A post change (a PATCH, or a create re-run with its external_id) for a post that is in a newsletter edition. Its text can no longer change. Nothing was changed. Events never answer this. Skip it and report it to the admin. Do not delete or retry.
409 cannot_continue A batch of direct messages for a conversation that people have used since it was moved (written, reacted, edited, deleted, reported or left), that has been removed, or that was sent with other participant_ids. Nothing was moved. Skip that conversation and report it to the admin. See Direct messages.
409 cannot_undo An undo of a moved conversation that people have used since. It stays as it is. Leave it and tell the admin. See Direct messages.
409 conflict Two requests made the same thing at the same moment, for example two creates with one external_id. Send the request again, once, after the other one has answered.
422 invalid A value is not allowed. details names each field and why, for example {"accessibility": ["is not included in the list"]} or {"photo": ["could not be fetched as an image"]}. Nothing was saved. Fix the value. When it is a choice for the admin (a name that is taken), ask them.
422 notify_needs_member_author A post, event or comment create with "notify": true and an author name from the old platform. Only a member can be the author of something that tells people. Nothing was made. Leave notify out (imports should not notify anyone), or, if the admin wants people told, use the member's author_id. See Posts. The same code answers an event create with "notify": true and an author name: see Events, and a comment: see Comments.
422 notify_needs_future_event An event create with "notify": true for an event that has already happened. Only an event still to come can be announced. Nothing was made. Leave notify out. Imports of past events should not notify anyone. See Events.
422 reply_to_another_thread A comment create with a parent_id of a comment that is on another post or event. Nothing was made. Give the post or event the parent is on. See Comments.
422 reply_too_deep A comment create that replies to a comment already nested 9 levels deep. Nothing was made. Attach the reply to the last comment that can take it, and tell the admin. See Comments.
422 reply_to_deleted A comment create that replies to a deleted comment. Nothing was made. Skip it and report it to the admin, or attach it to the post or event itself if the admin agrees. See Comments.
422 not_accepting_comments A comment create on a cancelled event. (A deleted post or event is 404 not_found.) Nothing was made. Skip it and report it to the admin. See Comments.
422 participants_not_found A direct-message conversation names a person (in participant_ids) or an author (author_id of a message) who is not a member of the community. details.participant_ids and details.author_ids list the ids you sent that did not match. Nothing was moved. Tell the admin who is missing. When they have joined, send the conversation again. See Direct messages.
422 not_allowed Rare: a person or an author in a direct-message conversation was banned from the community while the request ran. (A ban ends the membership, so otherwise the answer is participants_not_found. A block between them is never a refusal.) Nothing was moved. Skip that conversation and report it to the admin. See Direct messages.
422 older_than_imported A later batch of direct messages that starts before the newest message already moved. Nothing was moved. Send the batches in date order. See Direct messages.
422 confirmation_required A circle delete without confirm, or with a confirm that is not the circle's slug. Nothing was deleted. Only after the admin's clear yes: send the circle's slug as confirm. See Circles.
422 multiple_owners A circle delete for a circle that does not have exactly one owner. Nothing was deleted. Tell the admin. The circle's owners sort it out in the app first.
429 (see below) Too many requests. Wait the seconds in Retry-After, then go on, one request at a time.

What details names for posts, events, comments and claims

A 422 invalid answer names the field under details. For posts and claims these are:

details key Why
circle_ids Missing, empty, or not a list of circle ids.
content Missing or empty, not text, or (for a post sent with notify, or a change to a post not created through the API with an external_id) more words than the circle allows. It may be empty for a post with pictures sent without notify, and in a change to a post with pictures created through the API with an external_id.
author_id Not a member's id, or the member is not in every circle the post goes into.
author Not {"external_id", "name"} with an external_id of 1 to 200 characters (text or a number) and a name of 1 to 100, or sent together with author_id.
created_at Not an ISO 8601 time; a date that does not exist (30 February) or an offset beyond ±14:00; in the future, or before 1990. A time without an offset is read as UTC.
link_url Not an absolute http or https address of at most 2000 characters.
external_id Not text or a number, longer than 200 characters, or starting with fb: (kept for posts members bring from their own Facebook download).
images Not a list of files, or more than 10 pictures in all.
image_urls Not a list of addresses, or one that could not be fetched as a picture or is not JPEG, PNG or AVIF. The message names the address.
attachments A picture that is not a JPEG, PNG or AVIF, is larger than 10 MB, or cannot be read.
base The author has not completed their profile in the app (only for a post or comment sent with notify, or a change to a post or comment not created through the API with an external_id).
member_id A claim without a member_id, or with one that is not a member of this community.

Events use the same keys for circle_ids, author_id, author, created_at, external_id, images, image_urls and attachments, and add these:

details key Why
title Missing, empty, or not text.
starts_at Missing; not an ISO 8601 time with an offset such as +01:00 or Z (seconds are optional); a date that does not exist (30 February) or an offset beyond ±14:00; a plain date for an event that is not all-day; before 1990, or more than 50 years ahead. A moved start of an event not created through the API with an external_id may not be before 2020.
ends_at The same rules as starts_at, and not before the start.
time_zone Missing, or not an IANA time zone such as Europe/Copenhagen.
all_day, rsvp_enabled Not true or false.
meeting_url Not an absolute http or https address without spaces, of at most 2048 characters.
max_participants Not a whole number from 1 to 1,000,000, or null.
description Not text, or (for an event sent with notify, or a change to an event not created through the API with an external_id) more words than the circle allows. Word-limit errors on events are under description, never content.

Comments use author_id, author, created_at and external_id as posts do, and add these:

details key Why
post_id, event_id Neither is given, or both, or the value is not an id. On a create the comment goes on exactly one of them, and on a list the same.
parent_id Not an id of a comment, or the id of a comment the admin cannot read. Both answer the same, on purpose.
content Missing, empty, or not text. Comments have no word limit.
images, image_urls Sent at all: comments take no pictures.

Direct-message conversations name a field of a message by its place in the list, counting from 0, for example messages[3].created_at or messages[0].image_urls. Other keys are external_id, participant_ids (fewer than two people, or the same person twice), messages (not 1 to 100 messages, more than 20 photos in all, or, on a first import, not one message with text or a photo Zen Social can take) and the keys of a message: external_id (also when two messages share one), author_id, body (over 4000 characters, or empty without photos), created_at, images and image_urls. For a message, image_urls is refused only for an address that cannot be fetched, is over 5 MB or gives no image at all: a photo of another type (a GIF, say) is left out and counted in skipped_files, not refused. The answer never repeats the text.

For every list (GET /circles, /members, /posts, /events, /comments), a page beyond the last possible page is 422 invalid under details.page. A page that is merely past the end of the data is an empty list, not an error. And a null byte (the character \u0000) in any value of any request is 422 invalid, under the field it was found in.

Too many requests: 429

At most 300 requests a minute per key, and 600 a minute from one network address. Above that the answer has the header Retry-After: 60 and a body without a stable code:

{"error":"Too many requests. Please try again later."}

Wait that many seconds before the next request.

The same answer comes when a key sends more than 1,000 direct messages in a minute (POST /conversations, counted by the message, accepted or not). Wait, then send the same request again.

An address that does not exist

A path the API does not have, such as /rota in this version, answers 404 with a web page, not JSON. That includes every GET under /conversations: direct messages cannot be read. Check the path against these pages.

Server errors: 500

Something went wrong on Zen Social's side. Stop, tell the admin what you were doing, and try again later. If a create fails like this, sending it again with the same external_id is safe.