DaySurface
REST API

Tools

Call the Gmail, PDF, and webhook tools over the REST API

Every tool in the MCP surface is also a POST endpoint - same pure function, same input and output schemas, no behavioral drift between transports. Requires the services:execute scope.

POST /api/v1/services/{tool_name}

user_id is filled in from the authenticated principal, so never send it in the body.

Tools that change state (gmail_compose, gmail_send, gmail_update_draft, gmail_archive_thread, the pdf_* and webhook_* writers, and the rest of the mutating set) require an Idempotency-Key header. The response is replayed on retries with the same key, and the same key with a different payload is a 422. See Idempotency.

The full request and response schema for every tool is in the generated OpenAPI document at /openapi.json. The examples below cover the common paths.

gmail_status

Check whether an account is linked.

curl -X POST https://api.daysurface.com/api/v1/services/gmail_status \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'

Response:

{
  "connected": true,
  "email": "[email protected]",
  "scopes": ["https://www.googleapis.com/auth/gmail.modify"],
  "granted_at": "2025-01-15T10:30:00+00:00"
}

gmail_curate_inbox

Rank recent inbox threads by a deterministic score.

curl -X POST https://api.daysurface.com/api/v1/services/gmail_curate_inbox \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"limit": 10}'

Request body:

FieldTypeDefaultDescription
querystring(none)Gmail search query to narrow the set
limitinteger10Threads to rank (1-100)

The MCP transport renders this as an inbox dashboard; over REST you get the same ranked payload as JSON.

gmail_list_inbox

List recent messages, optionally filtered by a Gmail search query.

curl -X POST https://api.daysurface.com/api/v1/services/gmail_list_inbox \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"query": "from:[email protected] is:unread", "limit": 25}'

Request body:

FieldTypeDefaultDescription
querystring(none)Gmail search query
limitinteger25Messages to return (1-500)

gmail_get_thread

Fetch a thread with full message bodies.

curl -X POST https://api.daysurface.com/api/v1/services/gmail_get_thread \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"thread_id": "18f0c...", "strip_quoted_replies": true}'

Request body:

FieldTypeDefaultDescription
thread_idstring(required)Thread to fetch
include_attachment_databooleanfalseInline raw attachment bytes
strip_quoted_repliesbooleanfalseDrop quoted history from replies

Attachments always carry filename, mime_type, size_bytes, and attachment_id; fetch the bytes on demand with gmail_get_attachment.

gmail_compose

Create a draft. Mutating - send an Idempotency-Key.

curl -X POST https://api.daysurface.com/api/v1/services/gmail_compose \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: 0f1c2b3a-..." \
  -H "Content-Type: application/json" \
  -d '{
        "to": "[email protected]",
        "subject": "Contract - Thursday works",
        "body": "Thursday at 2pm suits us. Sending an agenda ahead of time."
      }'

Request body:

FieldTypeDefaultDescription
tostring(required)Comma-separated recipients
subjectstring(required)Subject line
bodystring(required)Message body
cc / bccstring(none)Comma-separated address lists
in_reply_to_thread_idstring(none)Thread to attach the draft to
attachmentsarray[]{ filename, mime_type, data_base64 }

Response: the draft's saved state - draft_id, thread_id, recipients, subject, a body preview, and the attachment list.

gmail_send

Send a draft that already exists. Mutating - send an Idempotency-Key.

curl -X POST https://api.daysurface.com/api/v1/services/gmail_send \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: 7d2e9f01-..." \
  -H "Content-Type: application/json" \
  -d '{"draft_id": "r-482..."}'

Request body:

FieldTypeDescription
draft_idstringThe draft to send

There is no "compose and send in one call" endpoint. Sending always references a draft, so the content exists and is reviewable before it goes out.

Operational tools

The server also exposes its own maintenance services over the same route shape:

ToolDescription
config_showShow the full configuration
config_getGet a single value by dot-separated key
config_setWrite a config override (mutating)
doctorRun environment health checks
doctor_fixRun health checks and auto-fix what it can (mutating)
curl -X POST https://api.daysurface.com/api/v1/services/config_get \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"key": "llm_config.cache_enabled"}'
{ "key": "llm_config.cache_enabled", "value": true }

目次