Messages
Send Message
Send a plain-text message into an existing conversation you participate in.
POST
Sends a plain-text message into an existing conversation you participate in. The same membership, rate-limit, and content-policy checks as the in-app messaging flow apply. This endpoint never creates conversations — get a
conversationId from GET /messages or by starting a proposal thread.
Requirements: messages:write scope + the public_api_messaging_writes feature + a claimed account (unclaimed accounts get 403 with details.reason: "account_claim_required" and a claimUrl).
Request
Existing conversation you participate in.
Plain-text message, 1–10,000 characters.
Response
Returns201 on success.
true on success.The created message, in the same shape as the message entries on
GET /messages: {id, createdAt, updatedAt, conversationId, jobId, senderUserId, isFromViewer, content, messageType, system, hasUnreadFlag, attachmentCount, hasAudio, editedAt, reactionCount}.Errors
| Status | code | Meaning |
|---|---|---|
400 | BAD_REQUEST | Empty body, invalid JSON, or invalid fields (details.issues carries zod details — e.g. content empty or over 10,000 chars) |
401 | UNAUTHORIZED | Missing or invalid token |
403 | FORBIDDEN | Missing messages:write scope, feature disabled, account not claimed (details.reason: "account_claim_required", details.claimUrl), not a participant in the conversation, or content blocked by policy (details.reason: "message_policy_blocked" with details.policy) |
404 | NOT_FOUND | No such conversation |
409 | CONFLICT | Send blocked — details.reason is employer_first_message_required (candidate replying before the employer’s first message), read_only_conversation, or message_blocked |
429 | RATE_LIMITED | Messaging rate limit hit — retry after details.retryAfterSeconds |