Posts
Creating, reading, and deleting posts and replies. Paths, scopes, request and response shapes.
Create a post or a reply
POST /public/v1/posts
Scope posts.write — OAuth 2.1 token or personal access token.
Send audience for a top-level post — it is required and never defaulted. Send in_reply_to_post_id for a reply, and then audience is forbidden: a reply inherits the thread visibility. Reply text is capped at 2000 characters against 5000 for a top-level post. Pass an Idempotency-Key header so a retry cannot double-post. media_ids is validated but not yet usable: the media endpoints are not part of this release, so any id supplied is rejected with 422 MEDIA_NOT_FOUND. Post without media until they ship.
Headers
| Name | Type | Required | Notes |
|---|---|---|---|
Idempotency-Key | string | no | Send the same key to retry a create safely: the first response is stored and replayed verbatim with Idempotency-Replayed: true. Reusing a key with a different body is 422; a duplicate still in flight is 409. At most 255 characters, matches ^[!-~]{1,255}$ |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
text | string | yes | 1–5000 characters |
audience | everyone | following | only_me | no | — |
media_ids | array of string (uuid) | no | at most 5 items |
in_reply_to_post_id | string (uuid) | no | — |
Example
curl -X POST "https://api.mouve.it/public/v1/posts" \
-H "Authorization: Bearer $MOUVE_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"text":"Hello from the Mouve API","audience":"everyone"}'Response 201
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | — |
text | string | yes | — |
author | PublicUserRefDto | yes | — |
created_at | string | yes | ISO 8601 timestamp in UTC. |
audience | everyone | following | only_me | yes | — |
in_reply_to_post_id | string | null | yes | The post this one replies to; null on a top-level post. |
media | array of PublicMediaRefDto | yes | — |
metrics | object | yes | — |
metrics.like_count | number | yes | — |
metrics.reply_count | number | yes | — |
metrics.repost_count | number | yes | — |
metrics.view_count | number | yes | — |
permalink | string | yes | Absolute URL, computed server-side: {APP_URL}/{username}/post/{id}. |
app_id | string | null | yes | Provenance from metadata.agent; null = first-party. |
Errors
Beyond the shared responses:
| Status | Code |
|---|---|
400 | VALIDATION_FAILED |
400 | INVALID_IDEMPOTENCY_KEY |
409 | IDEMPOTENCY_IN_FLIGHT |
422 | IDEMPOTENCY_BODY_MISMATCH |
422 | AUDIENCE_UNAVAILABLE |
422 | MEDIA_NOT_FOUND |
Get a post
GET /public/v1/posts/{id}
Scope posts.read — OAuth 2.1 token or personal access token.
Reads a single post by id, visibility-checked for the authorizing user. A post that exists but is not visible to them is a 404, identical to one that never existed — the API does not confirm the existence of content you cannot read.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
id | string (uuid) | yes | — |
Example
curl -X GET "https://api.mouve.it/public/v1/posts/11111111-2222-4333-8444-555555555555" \
-H "Authorization: Bearer $MOUVE_TOKEN"Response 200
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | — |
text | string | yes | — |
author | PublicUserRefDto | yes | — |
created_at | string | yes | ISO 8601 timestamp in UTC. |
audience | everyone | following | only_me | yes | — |
in_reply_to_post_id | string | null | yes | The post this one replies to; null on a top-level post. |
media | array of PublicMediaRefDto | yes | — |
metrics | object | yes | — |
metrics.like_count | number | yes | — |
metrics.reply_count | number | yes | — |
metrics.repost_count | number | yes | — |
metrics.view_count | number | yes | — |
permalink | string | yes | Absolute URL, computed server-side: {APP_URL}/{username}/post/{id}. |
app_id | string | null | yes | Provenance from metadata.agent; null = first-party. |
Errors
Only the shared responses.
List replies to a post
GET /public/v1/posts/{id}/replies
Scope posts.read — OAuth 2.1 token or personal access token.
Newest first, cursor-paginated. The parent post must be visible to the authorizing user: an unreadable thread is a 404 here exactly as it is on the post itself, so a hidden thread never leaks through its reply list.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
id | string (uuid) | yes | — |
Query parameters
| Name | Type | Required | Notes |
|---|---|---|---|
limit | integer | no | 1–100, defaults to 20 |
cursor | string | no | at most 512 characters |
Example
curl -X GET "https://api.mouve.it/public/v1/posts/11111111-2222-4333-8444-555555555555/replies" \
-H "Authorization: Bearer $MOUVE_TOKEN"Response 200
| Field | Type | Required | Notes |
|---|---|---|---|
data | array of PublicPostDto | yes | — |
meta | object | yes | next_cursor is null on the last page. |
meta.next_cursor | string | null | yes | — |
Errors
Beyond the shared responses:
| Status | Code |
|---|---|
400 | INVALID_CURSOR |
Delete a post
DELETE /public/v1/posts/{id}
Scope posts.write — OAuth 2.1 token or personal access token.
Soft-deletes a post the authorizing user owns and returns 204 with no body. Deleting a post that belongs to someone else is a 403; a second delete of the same post is a 404, so it is not idempotent — treat the 404 as "already gone" rather than retrying.
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
id | string (uuid) | yes | — |
Example
curl -X DELETE "https://api.mouve.it/public/v1/posts/11111111-2222-4333-8444-555555555555" \
-H "Authorization: Bearer $MOUVE_TOKEN"Response 204
No body.
Errors
Beyond the shared responses:
| Status | Code |
|---|---|
403 | FORBIDDEN |