Overview
Messaging tools handle sending messages in conversations and retrieving media attachments.send_message
Send a message in an Arlo Health conversation.The read-first gate
You must reply with the latest conversation state in hand, so replies never ignore the AI’s or provider’s most recent message. Two ways to satisfy this:- Pass
lastSeenMessageId(the id of the newest message you’ve seen, from a priorget_conversation/wait_for_reply). Arlo treats it as an assertion about the conversation’s newest message: if it matches, the send goes through with no extra round-trip; if a newer message has arrived, the send is refused even if you read the conversation earlier in the session. - Or omit it and call
get_conversationfirst. That read marks the conversation read for your session and satisfies the gate as long as no newer message lands in between.
conversation — review it, then call send_message again. No separate get_conversation call is needed.
Sends and waits for the reply
By default (waitForReply: true) this sends your message and waits for the next reply in one call — the AI’s triage reply, a provider connecting, or a provider’s message. The wait resolves only on replies newer than the message it just sent, so it never hands back the question you were answering. It is event-driven and resumable: if it returns stillWaiting, call wait_for_reply to keep waiting (do not re-call send_message — that would re-send).
Set waitForReply: false only to fire-and-forget.
Parameters
Safe retries (idempotency)
Background loops and retrying runtimes can accidentally re-send a write into a medical record.send_message, start_conversation, and confirm_provider_connection accept an idempotency key: a replay of the same key within 24 hours returns the original result (tagged _meta["arlo.health/idempotentReplay"]) instead of executing again, and a replay while the first call is still running returns duplicate_in_flight. A failed call releases the key so a genuine retry re-runs.
Two ways to pass it:
- Runtime-level (preferred): set
_meta["arlo.health/idempotencyKey"]on the tool call — works on every host, invisible to the model. - Model-level: the
idempotencyKeyparameter, advertised only on agent runtimes.
Behavior by conversation status
Text messages — first person
Messages are delivered to the AI and provider as if the patient typed them. Write natural first-person responses (“I have…”, “My throat still hurts…”), never third person. For a child or dependent, stay first person from the account holder’s perspective (“My daughter’s fever is down today.”).Answering an informationNeed checklist
When a prior wait_for_reply / get_conversation returned an informationNeed list, that’s the full set of what triage still needs. Gather those answers from the patient, then send them all in one call (use the messages array to batch) — this collapses many triage round-trips into one.
Two hard rules:
- Answer
redFlagGateitems first. - Only report what the patient actually said. Never guess or fabricate a clinical answer — these messages become part of a medical record. If the patient doesn’t know, say so in first person (“I’m not sure how long it’s been”).
Photo/Video messages
Behavior depends on the host:- Widget hosts (Claude.ai, ChatGPT): sending media yourself generally doesn’t work — these hosts usually give the model no access to the user’s files, so the bytes have to come from their device. Tell the user to tap the + button in the Arlo consultation panel to upload from their camera or camera roll, and don’t offer to attach media you can’t read.
- Agent runtimes with file access (Claude Code, custom agents): send media via the
mediaobject. Preferred: put the file bytes as base64 inmedia.data— the server uploads and sends it for you. Or omitmedia.datato receive a short-lived (~60s) presigneduploadUrland PUT the file yourself.
media.data still works — the constraint is file access, not the host.
Returns
The shape varies by conversation status and options:
Because the wait is folded in, a send can return any
wait_for_reply shape — including note, nextStep, and nextStepArgs you can pass straight through.
Triage completed — payment required
This is the transition every consultation hits: your send finished triage, so the same call comes back holding the payment gate instead of a reply.paymentGate.consultationSummary to the patient, then call confirm_provider_connection once they agree (paymentType: "pay_per_use"), or create_payment_setup first when it is payment_setup_required. Arlo never auto-charges.
Read-gate refusal
conversation, adapt your message to it if needed, then retry with lastSeenMessageId: latestMessageId.
get_media_url
Retrieve media attached to a message (photos, videos, or files). Messages fromget_conversation show the media type but not the attachment itself — this tool fetches it.
Parameters
Behavior by media type
Signed URLs expire (typically in 1 hour).