Send a message to a site
send_message
Sends a prompt to the agent that builds a site, starting a generation at once or adding it to the queue when the site is busy.
Every message spends AI credits from the workspace that owns the site, so batch your instructions into one clear prompt rather than many small corrections. Use it for every instruction to the site agent, including answering a question the agent asked and approving or changing a plan. A 402 means the workspace has no AI credits left.
When the site is free, the generation starts at once and the response carries queued:false and a jobId. Poll get_job with that jobId until its state is no longer running.
Only one generation runs per site at a time, and this tool never fails because of that. When a generation is already running, or the last one was stopped, failed or ended part way through, the message is queued instead. The response then carries queued:true, the entry's queueItemId, the reason it was held in queueReason, whether that hold is resumable in queueResumable and its place in line in queuePosition. That is a success, so the tool tells the client not to resend it or retry in a loop.
With queueResumable:false the queue runs in order on its own as soon as the site is free. With queueResumable:true the queue stays parked on purpose, and nothing runs until somebody has looked at what went wrong: read the failure with get_job, then call resume_queue. An answer to the agent's question and a reply to a plan never queue, whatever is already waiting, because the agent is blocked until you reply.
Request
Call it with a POST to https://api.modulify.ai/v1/send_message, sending the inputs below as a JSON object. The token needs the chat:write scope.
It makes changes, so send an Idempotency-Key header whenever you might retry it. A retry with the same key gets the first answer back instead of running again.
curl -X POST https://api.modulify.ai/v1/send_message \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"projectId":"PROJECT_ID","prompt":"PROMPT"}'Over MCP, the same method is the send_message tool.
When it is refused
A full queue is refused. A site holds at most 20 waiting messages and at most 8 from any one person, and going over either comes back as a 429 saying which cap was hit. The tool tells the client to remove an entry with remove_queued_message rather than retrying.
The message is also refused rather than queued while the site is locked because the workspace holds more sites than its plan allows. The same goes while the GitHub repository behind the site blocks editing: while its code is being moved into GitHub or back to Modulify, when Modulify has lost access to the repository or it was archived or deleted, and when the code lives in the workspace's own GitHub but the workspace is on the Free plan. See Site chat and Message queue.
Inputs
| Input | Type | Required | Description |
|---|---|---|---|
projectId |
string | Yes | The site id, from list_sites or get_site. |
prompt |
string | Yes | What the site agent should do, as specific as an instruction to a developer. It must not be empty and is refused over 500,000 characters. |
mode |
string | No | build, where the agent makes the change, or plan, where it proposes a plan for you to approve first. Defaults to build, and any other value is refused. |
Response
Every call answers with the JSON envelope of success, message, data, code and version. data holds the result described above, and on a method that returns a total, count carries it. The response headers carry the call's X-Request-Id and what is left of your per-minute budget in X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Errors explains every status code a call can answer with.