On this page

Developer docs / REST API

Your automation. One conversation.

Send a message, receive a reply, and follow approved work through to its result. Build with the Pushyou REST API.

Set up your connection

01Before you start

Start with an account and a conversation in the Pushyou internal test app you were invited to. Web sign-in requires QR approval in that app. The setup guide lets you choose a conversation and verify delivery.

Create a program connection in Settings → Connections. Grant only the conversations and operations it needs. Keys are shown once. Selected-room access cannot create a new room; that requires all-room access and conversations:write.

02Authentication

Run these commands in Bash or zsh on your computer or server. Store the key in your runtime’s secret configuration. Never embed it in browser code, a URL, or a published conversation view. MCP OAuth tokens cannot authenticate REST requests.

Production endpoints
export PUSHYOU_API_URL='https://asia-northeast3-pushyou-prod.cloudfunctions.net/pushyouApi'
export PUSHYOU_MEDIA_URL='https://asia-northeast3-pushyou-prod.cloudfunctions.net/pushyouMedia'
Load your program key
printf 'Pushyou API key: ' >&2
IFS= read -r -s PUSHYOU_API_KEY
printf '\n' >&2
export PUSHYOU_API_KEY
Check account and scope
curl --fail-with-body --silent --show-error \
  -X GET "$PUSHYOU_API_URL/v1/me" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY"

03Send your first message

Choose an existing conversation

Check id, permissions and conversation_ids in the account response. A null conversation_ids value means all conversations. List permitted rooms, then replace YOUR_CONVERSATION_ID with one of their IDs. Reading this list requires conversations:read.

Choose an existing conversation
curl --fail-with-body --silent --show-error \
  -X GET "$PUSHYOU_API_URL/v1/conversations?limit=20" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY"
PUSHYOU_CONVERSATION_ID
export PUSHYOU_CONVERSATION_ID='YOUR_CONVERSATION_ID'
Send a message
curl --fail-with-body --silent --show-error \
  -X POST "$PUSHYOU_API_URL/v1/conversations/$PUSHYOU_CONVERSATION_ID/messages" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"id":"docs-message-01","text":"Pushyou connection verified.","notify":true}'
Example response · HTTP 201
{
  "conversation_id": "YOUR_CONVERSATION_ID",
  "message_id": "docs-message-01",
  "duplicate": false
}

Use a stable ID for each logical message. Sending the same ID and content again returns HTTP 200 with duplicate: true. A different payload for the same ID returns 409. Use a new ID for a new message.

Message fields

FieldContract
idRequired. 1–80 letters, numbers, underscores or hyphens.
textUp to 8,000 characters. Nonempty text or at least one attachment is required.
attachment_idsUp to four ready file IDs uploaded to this conversation.
cardOptional report, actions or html card. A card alone does not replace text or an attachment.
notifyBoolean, default true. Requests a push notification; delivery also depends on device permission and notification settings.

Conversation, message and history lists use limit (1–100, default 50) and before=next_cursor. Keep filters when paging; stop when next_cursor is null. Events instead use an ascending numeric after cursor. Reads do not mark messages as read in the app.

04Replies & execution

Reply in the app, then fetch events. After actually handling an ordinary reply or action, save the result, send its ACK, and persist next_cursor. ACK does not remove an event or claim exclusive execution. Use one ordinary responder per room, or coordinate consumers yourself.

Read replies
curl --fail-with-body --silent --show-error \
  -X GET "$PUSHYOU_API_URL/v1/conversations/$PUSHYOU_CONVERSATION_ID/events?after=0" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY"
ACK · YOUR_EVENT_ID
curl --fail-with-body --silent --show-error \
  -X POST "$PUSHYOU_API_URL/v1/conversations/$PUSHYOU_CONVERSATION_ID/events/YOUR_EVENT_ID/ack" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{}'

Request approval

Request approval
curl --fail-with-body --silent --show-error \
  -X POST "$PUSHYOU_API_URL/v1/conversations/$PUSHYOU_CONVERSATION_ID/tasks" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"id":"docs-report-01","title":"Review the weekly report","category_id":"reports","fields":[{"id":"notes","label":"Review notes","type":"multiline","required":false}]}'

Tasks track approval and execution together. After the owner approves, a worker reads events with task_protocol=1, persists its execution token, claims the attempt, and keeps the claim alive with heartbeats. It saves the actual result before complete. Task completion also acknowledges the event; a standalone ACK cannot finish a pending task.

Task fields, claims and result contract (English)

Response receiver

For continuous replies, run the Node 22+ receiver with a REST program key. It saves results before sending and acknowledging. Keep the process running; it cannot work while the computer is asleep. Its Codex mode starts a dedicated session and does not attach to an existing chat.

Webhooks

Signed outbound webhooks are also supported. Configure them in the mobile app’s Response webhooks settings. Verify the original body signature and deduplicate delivery IDs. HTTP 2xx confirms receipt, not task completion. The downloadable workflow guide includes the signature and retry contract.

05Files & views

Files use a separate media endpoint and the same REST program key with media:write or media:read. Upload original bytes first, then attach the returned ID to a message in the same room. File URLs are private and require authorization. Images are limited to 10 MiB; videos to 25 MiB.

Upload a local PNG, then attach it
curl --fail-with-body --silent --show-error \
  "$PUSHYOU_MEDIA_URL/v1/media/docs-image-01?conversation_id=$PUSHYOU_CONVERSATION_ID" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY" \
  -H 'Content-Type: application/octet-stream' \
  -H 'X-Pushyou-Filename: review.png' \
  --data-binary @review.png
Send a message
curl --fail-with-body --silent --show-error \
  -X POST "$PUSHYOU_API_URL/v1/conversations/$PUSHYOU_CONVERSATION_ID/messages" \
  -H "Authorization: Bearer $PUSHYOU_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"id":"docs-attachment-01","text":"Please review this image.","attachment_ids":["docs-image-01"]}'

MCP uses pushyou_prepare_media_upload. Supply media_id, filename, content_type, byte size and SHA-256. POST the original bytes to the returned upload.url with its exact file-specific headers within five minutes, then send attachment_ids. The same preparation is available through REST. Do not substitute an account key or MCP OAuth token for the upload grant.

Publish an optional report, actions or HTML view with PATCH …/view and {"view": CARD}; {"view": null} removes it. The view opens from its conversation. HTML is isolated: no external network, app credentials or browser storage. Declare actions explicitly; they return events to the same room.

Card / Field
Request shape
Card =
  { type: "report", title: string, items: { title: string, body?: string, label?: string }[] }
| { type: "actions", title: string, body?: string, actions: { id: ID, label: string }[] }
| { type: "html", title: string, html: string, height?: integer, actions?: { id: ID, label: string }[] }

Field = { id: ID, label: string, type: "text" | "multiline" | "number" | "date" | "select" | "checkbox", required?: boolean, options?: string[], min?: number, max?: number }

06Endpoint reference

Paths are relative to the base shown for each group. Use a Bearer program key unless a file-specific upload grant is specified. Expand a row for its request shape. Placeholder IDs must be replaced with your own.

JSON API

https://asia-northeast3-pushyou-prod.cloudfunctions.net/pushyouApi
POST/v1/conversations/{id}/media/uploads

Prepare a file upload

Provide the file ID, name, type, size in bytes and SHA-256 to receive a file-specific upload grant valid for five minutes. Requires media:write.

Request shape
{ media_id: ID, filename: string, content_type: string, size: integer, sha256: string }
GET/v1/me

Check account

Check the account and permissions associated with this API key.

Request shape
GET /v1/me
→ { id, key_scope, permissions, conversation_ids }
POST/v1/conversations

Create conversation

Create a conversation with an id and name. Retrying with the same ID and content does not create a duplicate.

Request shape
{ id: ID, name: string, description?: string, kind?: string, view?: Card }
GET/v1/conversations

List conversations

List conversations in your account.

Request shape
?limit=20&before=CURSOR
→ { conversations, next_cursor }
GET/v1/conversations/{id}

Get conversation

Get the conversation name, description, status and published view.

Request shape
GET /v1/conversations/ROOM_ID
→ { conversation }
PATCH/v1/conversations/{id}

Update conversation

Update the name, description and pinned state. Use active to pause or resume the conversation.

Request shape
{ name?: string, description?: string, pinned?: boolean, active?: boolean, view?: Card | null }
POST/v1/conversations/{id}/messages

Send message

Send text, cards and attachments. Include uploaded file IDs in attachment_ids and use notify to control push notifications.

Request shape
{ id: ID, text?: string, attachment_ids?: ID[], card?: Card | null, notify?: boolean }
GET/v1/conversations/{id}/messages

Read conversation

List incoming messages and user replies, newest first.

Request shape
?limit=20&before=CURSOR
→ { messages, next_cursor }
GET/v1/conversations/{id}/messages/{message_id}

Get message

Read the content and card of a single message.

Request shape
GET /v1/conversations/ROOM_ID/messages/MESSAGE_ID
→ { message }
GET/v1/conversations/{id}/events/head

Current response sequence

Read the latest sequence before starting a new receiver. This does not mark messages as read or events as handled.

Request shape
GET /v1/conversations/ROOM_ID/events/head
→ { cursor }
GET/v1/conversations/{id}/receiver-status

Get receiver status

Check the last reported receiver state and time. This does not confirm task completion or ownership of a task claim.

Request shape
GET /v1/conversations/ROOM_ID/receiver-status
→ { receivers, checked_at }
PUT/v1/conversations/{id}/receiver-status/{instance_id}

Report receiver status

Report each run with a new UUID and an increasing sequence. Requires conversation read, event read, ACK and message write permissions.

Request shape
{ sequence: positive_integer, state: "starting" | "receiving" | "processing" | "retrying" | "paused" | "stopped" | "needs_review" | "delivery_pending" }
instance_id: UUID v4
GET/v1/conversations/{id}/events

Get user responses

Read chat replies and submitted button actions after the given sequence.

Request shape
?after=0
?after=0&task_protocol=1
→ { events, next_cursor }
POST/v1/conversations/{id}/events/{event_id}/ack

Acknowledge a response

Mark an event as handled by your agent or server.

Request shape
{}
→ { ok: true }
GET/v1/conversations/{id}/view

Get conversation view

Read the HTML or component view published in a conversation.

Request shape
GET /v1/conversations/ROOM_ID/view
→ { view: Card | null }
PATCH/v1/conversations/{id}/view

Publish conversation view

Send a card as view to add an Open view control to the conversation header. Send null to remove it.

Request shape
{ view: Card | null }
GET/v1/history

Get history

Page through history using any combination of category_id, kind, status_group and conversation_id.

Request shape
?limit=20&before=CURSOR
&category_id=reports&conversation_id=ROOM_ID
&kind=received&status_group=attention
&result_pending=true
kind: received | action | saved
status_group: attention | progress | problems | complete
→ { history, next_cursor }
POST/v1/conversations/{id}/tasks

Request task approval

Send a stable task ID, title, body and form. An execution event is created only after the user approves.

Request shape
{ id: ID, title: string, body?: string, category_id?: ID, fields?: Field[], inputs?: object, expires_at?: integer, result_requires_review?: boolean }
GET/v1/conversations/{id}/tasks/{task_id}

Get task status

Read the current status, attempt, inputs and result.

Request shape
GET /v1/conversations/ROOM_ID/tasks/TASK_ID
→ { task }
GET/v1/conversations/{id}/tasks/{task_id}/versions

Read request versions

Review previous versions of the same request.

Request shape
GET /v1/conversations/ROOM_ID/tasks/TASK_ID/versions
→ { versions: [{revision, title, body, fields, inputs, created_at, change_request}] }
POST/v1/conversations/{id}/tasks/{task_id}/revise

Revise request

Return an updated request for owner approval.

Request shape
{ request_id: ID, expected_revision: integer, change_request_id?: ID, body: string, title?: string, fields?: Field[], inputs?: object }
Only pending or changes_requested tasks can be revised. Approval is required for every new version.
POST/v1/conversations/{id}/tasks/{task_id}/claim

Claim task execution

Save the execution token before claiming the attempt. Start external work only after the claim succeeds.

Request shape
{ request_id: ID, attempt: integer, execution_token: string }
POST/v1/conversations/{id}/tasks/{task_id}/heartbeat

Keep task claim alive

Renew the current claim's ten-minute lease. A lost lease leaves the task awaiting review.

Request shape
{ request_id: ID, attempt: integer, execution_token: string }
POST/v1/conversations/{id}/tasks/{task_id}/complete

Report task result

Save the result locally before reporting succeeded, failed or needs_review. The corresponding event is also acknowledged.

Request shape
{ request_id: ID, attempt: integer, execution_token: string, status: "succeeded" | "failed" | "needs_review", result: string, result_links?: [{label: string, url: HTTPS_URL}] }
GET/v1/conversations/{id}/actions

List conversation actions

Read the conversation's forms and their current revisions.

Request shape
GET /v1/conversations/ROOM_ID/actions
→ { actions }
PUT/v1/conversations/{id}/actions/{action_id}

Publish conversation action

Create or update a form. Updates require expected_revision. The user submits the form explicitly.

Request shape
{ title: string, description?: string, fields: Field[], category_id?: ID, active?: boolean, expected_revision?: ID }

Media API

https://asia-northeast3-pushyou-prod.cloudfunctions.net/pushyouMedia
POST/v1/uploads/{id}

Upload a prepared file

Send the original bytes using the URL and file-specific authorization header from MCP upload preparation. Do not substitute an account key or MCP OAuth token.

Request shape
POST upload.url
Headers: upload.headers
Body: original file bytes
POST/v1/media/{id}?conversation_id={room}

Upload photos or videos

Send original file bytes with a URI-encoded X-Pushyou-Filename header. Retry with the same ID and file.

Request shape
Content-Type: application/octet-stream
X-Pushyou-Filename: URI_ENCODED_FILENAME
Body: original file bytes
GET/v1/media/{id}

Get file

Fetch photos or videos with an authorization header. Video supports a single Range request.

Request shape
Range: bytes=0-65535 (optional)
→ original file bytes
HEAD/v1/media/{id}

File information

Check MIME type, size and Range response headers without downloading the file body.

Request shape
HEAD /v1/media/MEDIA_ID
→ Content-Type, Content-Length, Accept-Ranges
DELETE/v1/media/{id}

Delete unused upload

Remove a file that has not been attached to a message. Deleted file IDs cannot be reused.

Request shape
DELETE /v1/media/UNATTACHED_MEDIA_ID

07Troubleshooting

REST failures return a non-2xx status and {"error": "…"}. MCP tool failures are reported by the client; check the structured error and whether the connection still has permission.

400Validate IDs, required fields, types, cursor and request size.
401Authenticate again or check whether the key expired, was rotated or revoked.
403Check operation permissions, allowed rooms and whether the room is paused.
404Check the resource ID and endpoint base. MCP /mcp is a protocol endpoint, not a web page.
409Inspect the conflict: reused ID with different content, stale revision, task claim, or already-attached media. Do not blindly repeat external work.
410The conversation is being deleted or the resource is no longer available. Check its current state.
413Reduce the JSON body or file size; check the file upload limits.
415Use a supported photo or video format with valid file bytes.
416Check that the requested byte range is within the file.
429Back off on request limits. For media quota failures, free unused storage before retrying. New messages are limited to 60/minute per room.
500Retry transient failures with backoff and stable IDs. If external work may already have run, inspect its result before running it again.

Ready to connect your own conversation?

Choose your room, approve access, and check the first message and reply in guided setup.

Set up your connection