Skip to content

Send from Adobe Journey Optimizer

An Adobe Journey Optimizer (AJO) journey can send an Outreach to the profile it is handling, at any step, and branch on the result. You configure two AJO custom actions: a send action that asks your agent to send one Outreach to the profile, and a status action that reads back whether it went out. Each send is an ordinary send to one person, with the same consent, STOP and duplicate protection as any other. See Send to one person.

The send goes to the profile's mobile number on RCS. That number is the person's channel identity: the send reaches whoever holds it in Anychat, and creates a Person to hold it the first time. The AJO profile id and email never find anyone.

This document describes the AJO side of the setup. The fields and answers of both actions are in the API reference.

Before you begin

  • Write the Outreach the journey sends, with the inputs its opener needs, such as a check-in date. Each Outreach gets its own send action.
  • Connect an RCS channel to the agent. Until it is launched, add the phones you test with to its early-access list. See Early access and launch.
  • You need permission in Journey Optimizer to configure custom actions, and to create journeys.

Create an API key for AJO

  1. Open Workspace · API Keys and click Create API Key.
  2. Name it after the journey or the AJO sandbox it serves.
  3. Under Scopes, choose Adobe Journey Optimizer and nothing else. A key with only this scope can send and check outreach through these two actions, and it is refused everywhere else in the API.
  4. Copy the key. You paste it into both actions.

Generate the action definition

Anychat generates what you enter in AJO for each Outreach: the action names, URLs, header, the payloads to paste, and how to mark every field.

The URL needs three ids. Open the agent's Settings · API page, which lists the organization id, the agent id and every Outreach's id, each with a copy button. An Outreach's id is also at the top of its own page. An Outreach id is often a short name such as reservation, not a random string.

Fetch the definition with the key you just created:

curl -s https://api.anychat.ai/v1/orgs/{org}/agents/{agent}/integrations/ajo/outreach/{outreachId}/action-definition \
  -H "Authorization: Bearer $ANYCHAT_API_KEY"

send and status hold one action each, and runbook lists the setup and branching steps in order. The definition is generated from the Outreach, so its inputs are exactly the ones the Outreach needs. The steps below say where each part goes. See The action definition for every field.

Configure the send action

Create a custom action in Journey Optimizer (Adobe's guide: Configure a custom action).

  1. Name it send.name, for example anychat_arrival_info. The name is how the journey reads the response, as @action{anychat_arrival_info.outcome}.
  2. Set the URL to send.url, your agent's send path for the Outreach:

    https://api.anychat.ai/v1/orgs/{org}/agents/{agent}/integrations/ajo/outreach/{outreachId}/sends
    
  3. Set the method to POST.

  4. Add a constant header Authorization with the value Bearer <your API key>.
  5. Paste send.request as the request payload. It has one entry under inputs for each of the Outreach's inputs, like this:

    {
      "profile": { "phone": "", "channel": "rcs", "id": "", "firstName": "", "lastName": "", "email": "" },
      "profileUpdate": "fill",
      "consent": { "sms": "", "evidence": "" },
      "journey": { "versionId": "", "nodeId": "", "instanceId": "" },
      "inputs": { "checkInDate": "", "marina": "" },
      "emulator": { "personaId": "" },
      "dryRun": false
    }
    
  6. Mark each field as send.fields says:

    Field Mark as
    profile.channel Constant, rcs
    profileUpdate Constant, fill: a send fills in only the profile fields Anychat doesn't have yet. Set overwrite when AJO is the source of truth for names and email
    emulator.personaId Constant, empty. See Rehearse without a phone
    dryRun Constant, false
    Everything else Variable. Tick Is optional on every field except profile.phone and the inputs the opener needs
  7. Paste send.response as the response payload:

    { "outcome": "queued", "reason": "", "detail": "", "sendId": "osnd_0123456789abcdef01234567", "personId": "665f2a0000000000000000", "unknownInputs": [], "idempotency": "journey" }
    
  8. Tick Define a failure response payload and paste send.errorResponse:

    { "code": "", "title": "", "detail": "" }
    

Once a journey that uses the action is live, AJO lets you change only the action's name, description, URL and authentication. The payload is frozen. Anychat keeps both actions compatible for as long as a journey calls them.

Configure the status action

Create a second custom action, status.name (anychat_send_status). One status action serves every Outreach on the agent, so create it once.

  1. Set the URL to status.url, https://api.anychat.ai/v1/orgs/{org}/agents/{agent}/integrations/ajo/sends.
  2. Set the method to GET, and add the same Authorization header.
  3. Paste status.response as the response payload:

    { "sendId": "osnd_0123456789abcdef01234567", "state": "sent", "reason": "", "reachable": true, "delivered": false, "read": false, "replied": false, "final": true, "nextAttemptAt": "" }
    
  4. Tick Define a failure response payload and paste the same { "code": "", "title": "", "detail": "" } example.

In the journey, the status action is given the send's id in its Path, status.path (@action{anychat_arrival_info.sendId}), so it calls …/sends/<sendId>. If you prefer, add a variable query parameter sendId to the action instead; the endpoint accepts either.

Add the actions to a journey

  1. Add the send action where the journey should send.
  2. Map its variable fields:

    Field Map to
    profile.phone The profile's mobile number, such as mobilePhone.number. It must be in E.164 form (+19285551234), or the send answers 400
    profile.id Journey Properties › profileId
    journey.versionId Journey Properties › journeyVersionUID
    journey.nodeId Journey Properties › currentNodeId
    journey.instanceId Journey Properties › instanceUID
    consent.sms The profile's consents.marketing.sms.val
    inputs.* The event or profile fields that hold each value

    Pick the Journey Properties from the expression editor rather than typing them. The three journey fields make each send happen once per step per profile: if AJO retries the call, the retry is recognized and nothing is sent twice. Without them, a profile gets this Outreach at most once a day. profile.id serves only that daily fallback, and isn't stored.

  3. Tick Add an alternative path in case of a timeout or an error. A profile whose call fails without this path leaves the journey.

  4. Leave the journey's timeout for external calls at 30 seconds. AJO retries a failed call within that time, but it doesn't retry a call that runs past the timeout.

Branch on the outcome

Every outcome of the send action arrives on the success path, so branch on @action{anychat_arrival_info.outcome}:

Outcome What to do
queued or duplicate The send is under way. Wait, then check it with the status action
refused Don't reach the person by another route. reason says why: the number replied STOP, consent.sms was n, the person's consent is recorded as denied, or an input is missing
skipped A dry run; nothing was sent

Treat any reason your journey doesn't recognize as a refusal. The error path is for calls that failed outright: a wrong key or URL, a disabled Outreach (404), a malformed payload or a phone number not in E.164 form (400), or the API being unavailable. After an error or a timeout, the send might still have been accepted, so check before reaching the person another way.

For the status action, wait, then read it and branch:

  • reachable is false: the person's phone can't receive RCS. Reach them on another channel.
  • final is false: the send is still queued or being retried. Wait and read it again. A temporary failure is retried for about 30 minutes before the send is marked failed, and nextAttemptAt says when the next attempt is due.
  • state is sent: the message went out. delivered and read are best-effort: false doesn't mean it failed.
  • state is failed and reason is binding_error: the agent's RCS channel is misconfigured. Route it to the error branch and tell whoever manages the agent. The person is still reachable.

Test the journey

Rehearse without a phone

Copy the send action and set its emulator.personaId constant to a persona's id, then leave the profile fields unmapped. A persona's id is at the top of its page under Test · Personas. The whole send runs, and the opener lands in that persona's emulator session, where you can reply to it. Nothing reaches a phone. The persona's own consent state applies, so you can rehearse a refusal with a persona that has opted out.

Send to your own phone

To see the real message on a real phone, call the send action yourself with your own number in profile.phone and no emulator block. Your phone must be on the RCS channel's early-access list until the channel is launched.

curl -s -X POST https://api.anychat.ai/v1/orgs/{org}/agents/{agent}/integrations/ajo/outreach/{outreachId}/sends \
  -H "Authorization: Bearer $ANYCHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile": { "phone": "+15551234567", "firstName": "Your name" },
    "consent": { "sms": "y" },
    "journey": { "versionId": "test", "nodeId": "send-1", "instanceId": "run-1" },
    "inputs": { "checkInDate": "2026-10-14" }
  }'

Then read the outcome with the status action. To send again, change journey.instanceId; the same three journey values answer duplicate and send nothing.

This is real data

A send to your own phone is a real send on the live agent, not a rehearsal. It reaches the Person who holds your number on RCS, or creates one, keeps the inputs on that record, and starts a real conversation that is listed, counted and billed like any other. If you reply STOP, your number is refused from then on. Use an obvious test name so you can find the record afterwards, and delete it on the People page when you are done. To try a journey with no real data at all, rehearse with a persona.

Check the wiring

Set dryRun to true in a copy of the action to check that a real profile resolves, that its consent allows the send, and that every input is mapped. It answers skipped or the refusal the real send would get, and nothing is written.

Test in AJO

AJO's test mode calls both actions for real, so test with profiles whose phones are on the channel's early-access list. Test mode shortens wait nodes, so the status action runs sooner than it would live and can read an earlier state. AJO's Dry Run skips custom actions entirely.

How many sends a key can make

A key can call the send action 12,000 times a minute (200 a second), in bursts of up to 1,000. This budget is the send action's own; the key's other sends through the API don't use it.

A journey triggered one event at a time, such as a booking or a check-in, never comes close. A journey that reads a whole audience can release more profiles at once than a burst allows. For that journey, turn on AJO's throttling for the endpoint: its lowest rate, 200 calls a second, is exactly this limit, so every profile gets through. Without throttling, calls over the limit are answered 429, AJO retries them only within its timeout, and the rest take the error path.

Rotate the key

AJO keeps an action's authentication editable after go-live. Create a new key with the Adobe Journey Optimizer scope, update the Authorization header on both actions, then delete the old key on Workspace · API Keys. See Manage access.

What's next