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 connection01Before 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.
export PUSHYOU_API_URL='https://asia-northeast3-pushyou-prod.cloudfunctions.net/pushyouApi'
export PUSHYOU_MEDIA_URL='https://asia-northeast3-pushyou-prod.cloudfunctions.net/pushyouMedia'printf 'Pushyou API key: ' >&2
IFS= read -r -s PUSHYOU_API_KEY
printf '\n' >&2
export PUSHYOU_API_KEYcurl --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.
curl --fail-with-body --silent --show-error \
-X GET "$PUSHYOU_API_URL/v1/conversations?limit=20" \
-H "Authorization: Bearer $PUSHYOU_API_KEY"export PUSHYOU_CONVERSATION_ID='YOUR_CONVERSATION_ID'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}'{
"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
| Field | Contract |
|---|---|
id | Required. 1–80 letters, numbers, underscores or hyphens. |
text | Up to 8,000 characters. Nonempty text or at least one attachment is required. |
attachment_ids | Up to four ready file IDs uploaded to this conversation. |
card | Optional report, actions or html card. A card alone does not replace text or an attachment. |
notify | Boolean, 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.
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"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
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.
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.pngcurl --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
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/pushyouApiPOST/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.
{ 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.
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.
{ id: ID, name: string, description?: string, kind?: string, view?: Card }GET/v1/conversations
List conversations
List conversations in your account.
?limit=20&before=CURSOR
→ { conversations, next_cursor }GET/v1/conversations/{id}
Get conversation
Get the conversation name, description, status and published view.
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.
{ 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.
{ 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.
?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.
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.
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.
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.
{ sequence: positive_integer, state: "starting" | "receiving" | "processing" | "retrying" | "paused" | "stopped" | "needs_review" | "delivery_pending" }
instance_id: UUID v4GET/v1/conversations/{id}/events
Get user responses
Read chat replies and submitted button actions after the given sequence.
?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.
{}
→ { ok: true }GET/v1/conversations/{id}/view
Get conversation view
Read the HTML or component view published in a conversation.
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.
{ view: Card | null }GET/v1/history
Get history
Page through history using any combination of category_id, kind, status_group and conversation_id.
?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.
{ 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.
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.
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_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_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_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_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.
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.
{ title: string, description?: string, fields: Field[], category_id?: ID, active?: boolean, expected_revision?: ID }Media API
https://asia-northeast3-pushyou-prod.cloudfunctions.net/pushyouMediaPOST/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.
POST upload.url
Headers: upload.headers
Body: original file bytesPOST/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.
Content-Type: application/octet-stream
X-Pushyou-Filename: URI_ENCODED_FILENAME
Body: original file bytesGET/v1/media/{id}
Get file
Fetch photos or videos with an authorization header. Video supports a single Range request.
Range: bytes=0-65535 (optional)
→ original file bytesHEAD/v1/media/{id}
File information
Check MIME type, size and Range response headers without downloading the file body.
HEAD /v1/media/MEDIA_ID
→ Content-Type, Content-Length, Accept-RangesDELETE/v1/media/{id}
Delete unused upload
Remove a file that has not been attached to a message. Deleted file IDs cannot be reused.
DELETE /v1/media/UNATTACHED_MEDIA_ID07Troubleshooting
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.
400 | Validate IDs, required fields, types, cursor and request size. |
|---|---|
401 | Authenticate again or check whether the key expired, was rotated or revoked. |
403 | Check operation permissions, allowed rooms and whether the room is paused. |
404 | Check the resource ID and endpoint base. MCP /mcp is a protocol endpoint, not a web page. |
409 | Inspect the conflict: reused ID with different content, stale revision, task claim, or already-attached media. Do not blindly repeat external work. |
410 | The conversation is being deleted or the resource is no longer available. Check its current state. |
413 | Reduce the JSON body or file size; check the file upload limits. |
415 | Use a supported photo or video format with valid file bytes. |
416 | Check that the requested byte range is within the file. |
429 | Back off on request limits. For media quota failures, free unused storage before retrying. New messages are limited to 60/minute per room. |
500 | Retry 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