Authentication, request shapes, examples, and error responses.

Send a message to the same subscribed ChatGPT dot using ordinary HTTPS. The REST endpoint is useful for other AI agents, backend services, scripts, CI workflows, or applications that cannot speak MCP.

Endpoint: POST https://YOUR-VERCEL-HOST/v1/messages

Authentication

Every request needs a dedicated REST bearer token in the Authorization header:

Authorization: Bearer <DM_DOT_REST_API_KEY>
Content-Type: application/json

Set DM_DOT_REST_API_KEY as a strong, random Production secret in Vercel. It is separate from the MCP connection’s MCP_API_KEY and cannot be used interchangeably.

Do not embed the REST token in a browser bundle, mobile app binary, public GitHub Actions log, or webpage. For untrusted clients, call the API through your own authenticated backend. The REST endpoint intentionally does not enable permissive browser CORS.

Request body

{
  "message": "Please review the latest deployment and flag any issues."
}

message is a required string, trimmed of leading/trailing whitespace, containing between 1 and 16,384 UTF-8 bytes. The JSON request body has a 32 KiB limit. Unknown JSON fields are rejected; there is no dot_id or recipient field because v1 supports exactly one active subscription.

Example: curl

Use your own deployment hostname and export the API key securely in your shell (for example, from your OS keychain), rather than typing it into terminal history.

curl --fail-with-body \
  -X POST 'https://YOUR-VERCEL-HOST/v1/messages' \
  -H "Authorization: Bearer ${DM_DOT_REST_API_KEY}" \
  -H 'Content-Type: application/json' \
  -d '{"message":"A new build is ready for review."}'

Example: JavaScript / TypeScript (server-side)

const response = await fetch('https://YOUR-VERCEL-HOST/v1/messages', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.DM_DOT_REST_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ message: 'A new build is ready for review.' }),
});

const result = await response.json();
if (!response.ok) {
  throw new Error(`dm-dot HTTP ${response.status}: ${result.error?.code}`);
}
console.log(result.event_id);

Example: Go

payload := strings.NewReader(`{"message":"A new build is ready for review."}`)
req, err := http.NewRequest(http.MethodPost, "https://YOUR-VERCEL-HOST/v1/messages", payload)
if err != nil { log.Fatal(err) }
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("DM_DOT_REST_API_KEY"))

client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()
fmt.Println(resp.Status)

Response

When ChatGPT’s webhook returns HTTP 2xx, the REST API returns HTTP 202 Accepted:

{
  "status": "accepted",
  "event_id": "evt_0123456789abcdef"
}

This is not a receipt from the dot. The event ID identifies the webhook delivery. ChatGPT may process the event asynchronously; there is no status polling endpoint, completion callback, reply tool, or message history in this version.

Error responses

Errors are JSON objects in this form:

{
  "error": {
    "code": "no_subscription",
    "message": "No active dot subscription"
  }
}
HTTP status Error code Meaning
400 invalid_json, invalid_body, invalid_message Malformed JSON or invalid message
401 unauthorized Missing or incorrect REST bearer token
405 method_not_allowed Only POST is supported
409 no_subscription No dot has an active webhook subscription
413 payload_too_large Request exceeds the JSON body limit
415 unsupported_media_type Expected Content-Type: application/json
502 delivery_failed ChatGPT did not acknowledge webhook delivery
503 not_configured, storage_unavailable REST secret missing or Redis unavailable

Responses include Cache-Control: no-store. Incorrect credentials never appear in the response.

Delivery and retries

The REST endpoint uses the same SendMessage delivery logic as send_to_dot. A callback may be attempted up to three times for transient network failures, HTTP 429, or HTTP 5xx. Those attempts share one event ID. There is no durable outbox, so after ambiguous failures the caller may not know whether ChatGPT accepted the event.

Do not assume retries are exactly-once. For application-level deduplication, include a unique request identifier inside your message and have your receiving workflow recognize repeats. An Idempotency-Key HTTP header is not implemented in v1.

Security considerations

  • Keep the bearer token server-side and use HTTPS.
  • Do not turn the REST endpoint into a publicly callable browser proxy without your own authentication and rate limiting.
  • Anyone with the REST key can wake the subscribed dot; share it only with trusted applications.
  • Messages should be treated as untrusted inputs by the dot. Sending a message does not authorize unrelated actions.

See Security and MCP & Events reference for the underlying protocol.