Skip to main content
The AI Outbound API lets a partner ask Oliver to reach out to a specific client on the practice’s behalf. Oliver’s AI handles the conversation by text for a given purpose, such as booking an appointment or following up on a prescription refill. The first message can be a rich RCS message (a card with a picture and reply buttons) that falls back to plain text.

How it works

  1. Trigger — POST a request to /ai_outbound/requests identifying the client by contact_number (required, optionally with client_id) and the purpose of the outreach.
  2. Synchronous intake — Oliver validates the input, resolves the client, and runs suppression checks before responding. A queued request returns 201; a suppressed request returns 200 with a suppression_reason.
  3. Asynchronous dispatch — Oliver composes an opener and starts the conversation. The request moves through queued → in_progress.
  4. Outcome — when the conversation resolves, the request reaches a terminal status (completed, suppressed, or failed). If you supplied a callback_url, Oliver delivers exactly one outcome webhook — on any of the three terminal statuses, not only when an outcome is set. The outcome is also available anytime via GET /ai_outbound/requests/{id}.
Outreach is a single opener followed by a reply-driven conversation — Oliver sends no automated follow-up texts. The opener is your initial_message_script if you send one, otherwise Oliver writes it from the purpose, context and patients.

Rich openers (RCS)

Add content to open with a card, carousel, image or reply buttons. It takes the same shape and limits as the messages endpoint. See Writing card text for layouts that work on a phone. Clients whose phone supports RCS see the rich message, with the practice’s name, logo and verified badge. Everyone else gets the plain-text opener as an SMS. Either way, every reply, including a tap on a reply button, goes to Oliver’s AI, which carries on the conversation and books the visit. A service reminder for two pets, where a tap on Let’s find a time starts booking:
Card: Bella and Max are due for vaccines, with three reply taps

On iPhone. The practice's opt-out line follows the description.

  • patients tells the AI which pets the outreach is about and what each is due for, so one message covers the household.
  • initial_message_script is the plain text: the SMS for phones without RCS, and the bubble of content.type: "text". Leave it out and Oliver writes it, so you can send only reply buttons and let Oliver write the line.
  • opener_channel on the request and the outcome webhook says how the opener arrived: rcs or sms.
  • The clinic’s opt-out line is added to the plain text and to the card’s description.
  • media_id is an image you uploaded to Oliver; a hosted media_url works too. The opener keeps the image it had when Oliver accepted the request, even if you replace the file before the text goes out.

Messages or AI Outbound?

Use Messages for announcements and anything staff should answer. Use AI Outbound when you want the client’s reply to become a booking.

Purposes

Statuses

A request moves through a linear lifecycle: received → queued → in_progress → one of completed, suppressed, failed When a request is completed, its outcome is one of booked, opted_out, or no_response. A failed request carries undeliverable when the text bounced at the carrier, and a null outcome with an error_message when dispatch never completed. A suppressed request has no outcome — it has a suppression_reason.

Timing

Suppression

Suppression is a normal, auditable outcome — not an error. Oliver will suppress a request when contacting the client would be inappropriate, for example:
  • recent_contact — Oliver already contacted the client (by SMS or AI phone call, from any campaign) within the last 72 hours.
  • active_conversation — there is already an open AI conversation with the client.
  • sms_opted_out — the client has opted out of SMS.
  • voice_not_supported — the voice channel is not yet available.
Suppression detected at intake returns 200 with the suppression_reason on the response. Eligibility is also re-checked just before the message is sent, so a request accepted with 201 can still end up suppressed — see the outcome webhook for the full list of reasons.

Idempotency

Set partner_request_id to a unique value per logical request. Re-submitting the same partner_request_id returns the existing request rather than creating a duplicate, so retries are safe.

Rate limits

Each practice is limited to 120 requests per minute. Exceeding the limit returns 429 Too Many Requests.