Skip to main content
Glama

Generate message

generate_message

Registers one message generation and returns immediately with 202 Accepted. Poll the status resource named in Location (or honour Retry-After) until status is terminal; the finished message is embedded in that resource. The API composes text; it does not send it, and it does not replace the recipient's WhatsApp opt-in.

What the message may say

Every candidate fact about the recipient and the sender carries a tier. The message asserts only facts of tier verified. reported and estimated facts may influence which angle is chosen but are never stated as fact; unknown facts are never mentioned. The tier of each field is derived from the provenance the record already carries (field by field, from its source and verification evidence), not chosen by the model. Personal data - contacts, officers, phone numbers, e-mail addresses, social handles, and, for a sole trader, the person's own name and address - is never asserted. After generation, code re-checks that each claim in the text maps to fact_ids of verified facts; an unsupported claim is removed, or the request fails - it is never shipped.

Language

The message is written in the language of the recipient company's website main page. There is no caller override. The decision is reported on the result as language (ISO 639-1) and language_source. If the homepage language cannot be determined the request fails with error code language_undetermined and the credits are returned; the language is never guessed.

Unknown companies

If the recipient or sender company is not yet known, the status passes through enriching, which runs the existing discovery/validation pipeline for that single domain, then composing.

Cost and balance

There is exactly one debit, at POST, for the whole generation including any enrichment. Enrichment never debits again and never changes the balance mid-job; a generation that ends failed for any reason below, or canceled before composition starts, returns that one debit. 402 insufficient_credits is therefore only ever returned by POST.

Jurisdiction

Checked at POST against what is already known: 422 jurisdiction_excluded. A company whose jurisdiction only becomes known after enrichment is excluded late: the generation ends failed with error.error = jurisdiction_excluded and the credits are returned. The text is never composed for an excluded jurisdiction.

Validation done before 202

Invalid body or hostname: 400 invalid_request. sender.company_domain is the sender's own company domain: the service reads the sender's facts from the knowledge base by it and does not verify that the caller controls it. When sender or sender.company_domain is omitted, the caller's seller profile website is the sender; with no profile website either, the request is 400 invalid_request (sender.company_domain is required when no seller profile website is set). Recipient or sender in a policy-excluded jurisdiction: 422 jurisdiction_excluded. Insufficient credits: 402 insufficient_credits. constraints.max_characters that no message can meet: 422 max_characters_unmeetable; when this only shows during composition the generation ends failed with the same code and the credits are returned. callback_url must be an https URL on a public host: a URL resolving to a private, loopback, link-local, reserved, multicast or cloud-metadata address is rejected with 422 unsafe_url (the same rule as odoo_url on the tenant Odoo endpoint). The service being temporarily unable to accept work: 503 service_unavailable with Retry-After; nothing is debited.

Error codes

Every error uses the surface's single Error shape {error, message, request_id}. error is a snake_case code: invalid_request, missing_idempotency_key, idempotency_key_reused, request_in_flight, jurisdiction_excluded, insufficient_credits, unsafe_url, max_characters_unmeetable, not_cancelable, generation_expired, service_unavailable, and, on a failed generation only, the MessageFailureCode values: insufficient_verified_facts (too few verified facts exist to write anything true), enrichment_failed, language_undetermined, language_failed, claim_verification_failed, jurisdiction_excluded, max_characters_unmeetable, compose_failed, compose_timeout and timed_out. A failed generation always returns its credits.

Idempotency (draft-ietf-httpapi-idempotency-key-header-07)

Idempotency-Key is required. Missing: 400 missing_idempotency_key. Same key with a different payload: 422 idempotency_key_reused. Same key while the original request is still being processed: 409 request_in_flight. Same key with the same payload: 200 (not 202) with the EXISTING resource in its current state (same id, no second debit). A key is remembered for 24 hours from its first use; after that the same key starts a new generation.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "recipient": "<recipient>",
      "channel": "<channel>",
      "objective": "<objective>"
    },
    "name": "generate_message"
  }
}

Input Schema

NameRequiredDescriptionDefault
senderNoThe sender's own company domain. The service reads the sender's facts from the knowledge base by it; ownership is not verified. If sender is omitted, the caller's seller profile website is the sender; the profile's offering and description are used as sender facts. If sender.company_domain differs from the profile website, the profile is not used.
channelYes
objectiveYesWhat the message should achieve, in the caller's words.
recipientYes
constraintsNo
callback_urlNoRESERVED. Accepted and stored, but no callback is delivered yet; poll the status resource. Must be an `https` URL on a public host: private, loopback, link-local, reserved, multicast and cloud-metadata addresses are rejected with `422 unsafe_url` (same rule as `odoo_url` on the tenant Odoo endpoint).

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Far beyond the annotations: it discloses the async state machine (`enriching` → `composing`), that there is exactly one debit at POST and refunds on `failed`/cancelled jobs, the 24-hour Idempotency-Key memory with 200-vs-202 semantics, jurisdiction checks at POST and late exclusion after enrichment, and an explicit reserved `callback_url` that never fires. These are exactly the cost, retry, and side-effect facts an agent cannot get from `readOnlyHint: false` / `openWorldHint: true`. Minor tension with `idempotentHint: false` (retry-safety is keyed on a caller-supplied header, so repeated calls with fresh keys still double-debit) is explained rather than contradicted.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The operationally critical fact (202 + poll + not a send) is front-loaded, and markdown headings make the long body scannable. It is nonetheless verbose, with the same conditions restated several times (`jurisdiction_excluded` and `max_characters_unmeetable` each appear in a prose section and again in the error list, `unsafe_url` twice), which costs some readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a paid, asynchronous, legally-constrained generation call with no output schema, the description covers everything needed to call it correctly: request-time vs. generation-time failures, refund behavior, idempotency, and the results surfaced on the status resource (`language`, `language_source`, `fact_ids` verification). Nothing material is left for the agent to discover by trial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 50% schema coverage the description does real work: it supplies the meaning of `sender.company_domain` (read from the knowledge base, ownership unverified, seller-profile fallback and the mismatch rule), states explicitly that there is NO language field because language derives from the recipient's homepage, and repeats the unsafe-URL rule for `callback_url`. It leaves `channel`, `tone`, and `objective` semantics largely to the schema, so it is strong but not exhaustive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a specific verb and resource ('Registers one message generation and returns immediately with 202 Accepted') and immediately bounds the scope: 'The API composes text; it does **not** send it, and it does not replace the recipient's WhatsApp opt-in.' That separates it from the sending/CRM-oriented siblings and from the polling siblings it points to ('Poll the status resource named in `Location`').

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage context is unusually explicit: when to call (compose a message), what to do after (poll `Location` or honour `Retry-After`), and what not to expect (no language override, no sending). It stops short of naming sibling tools such as `get_message_generation` or `cancel_message_generation`, so the agent must infer which tool implements 'the status resource' or cancellation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources