# How-to — send a lead a file: link, image, audio, video, PDF

> **You want.** *"enviar PDF documento recurso cuando el lead manda una palabra clave"* ·
> *"agent send image photo media resource attachment outbound picture"* · *"resource sending audio
> mid-conversation agent envía audios cómo funciona"* · *"creator resources upload image audio mid
> conversation"* · *"enviar fotos recursos multimedia agente cliente"* · *"whatsapp catalog product
> photos images resources the agent can send mid conversation"* · *"resourceSending
> update_resource_sending usage_context agent decides send audio clip"* · *"canned responses trigger
> audios respuesta enlatada cuando usar"* · *"get_resources get_case_study tools not enabled"*.

## The honest answer: the asset type decides the mechanism, not you

There is no single "send a file" call. Five separate paths exist, each covering a different
combination of asset type and timing, and **the agent itself can only ever send two types**.

| You want to send | When | Mechanism | Agent can do it on its own |
|---|---|---|---|
| A **link / URL** (incl. a PDF hosted somewhere) | any time | plain text in the prompt or examples | **yes** — no configuration at all |
| An **image** or an **audio** clip | the agent decides mid-conversation | Creator resource + `resourceSending` | **yes**, once enabled |
| An **audio** clip | a keyword or ad trigger fires | trigger audios | no — the trigger sends it |
| Fixed **text / a URL** | the *first* keyword match in a conversation | trigger canned responses | no — sent instead of the LLM |
| A **PDF / document / video** | a follow-up fires | follow-up workflow with a resource response | **no** — either a link, or the follow-up sends it |

**If you are here about a PDF, stop and read that last row.** The agent has no tool that can send a
document or a video, in any channel, under any configuration — `send_resource` rejects them
outright. Your two options are a **link to the file** (works everywhere, needs nothing, and is what
almost everyone actually wants) or a **follow-up workflow**, which is time-triggered and therefore
cannot answer "send it when the lead asks".

Links are the boring answer and usually the right one. The runtime appends a `utm_term` carrying the
workspace's configured identifier (the contact id unless it was switched to username; `xcod` on
Hotmart), so tracking works without writing `[[CONTACT_ID]]` anywhere — unless link tracking was
narrowed or switched off, the host is always-skipped, or the URL already carries the param. The one
place that spells out the whole contract is `platform-features.md` §Analytics & Tracking; keep it
there rather than restating it.

---

## The sequence

### A — image or audio, sent by the agent mid-conversation

**1. Upload the clip.** Three steps, because the bytes never pass through the model:

```text
create_resource_upload_url   name="bienvenida.m4a", type="AUDIO", content_type="audio/mp4"
                             → { upload_url, s3_key }
curl -T bienvenida.m4a -H 'Content-Type: audio/mp4' "<upload_url>"      # you run this
register_creator_resource    name="bienvenida.m4a", type="AUDIO", s3_key=<s3_key>,
                             content_type="audio/mp4",
                             usage_context="cuando el lead pregunta cómo funciona el programa"
```

`name` must carry the extension. **AUDIO must be M4A/AAC — MP3 is rejected** (Meta will not deliver
it). IMAGE must be JPG/PNG/GIF.

`usage_context` is the sentence the agent reads to decide when to send this. It is not optional in
practice: see the invariant.

**2. Turn sending on for the agent.**

```text
update_resource_sending  agent_id=<id>,
                         enabled=true, allowed_types=["IMAGE","AUDIO"], max_per_conversation=1
```

This writes `config.resourceSending` **directly**. It is not a v5Config field, does not bump a
prompt version, and sits outside the `deploy_agent_sdk` contract — so a later deploy will not
carry it and will not clobber it.

Per-field PATCH, **except `allowed_types`, which is replaced rather than merged.** Sending
`["AUDIO"]` to an agent that already allowed `["IMAGE","AUDIO"]` turns image sending off, silently
and immediately. Read `config.resourceSending` back with `get_agent_config` first and resend the
full set you want.

**3. Verify with `list_creator_resources agent_usable_only=true`** — the resource must appear.

### B — audio on a keyword trigger

Upload as in A, then attach it to the trigger. Keyword and ad triggers only.

```text
sync_trigger_audios  type="keywords", agent_id=<agent_id>, trigger_id=<id>,
                     audios=[{resource_id:<id>, order:0}]
```

`sync_trigger_audios` reconciles to the list you send: **anything absent from it is deleted.** Call
`list_trigger_audios` first and resend the full set. Several audios on one trigger play in rotation.

### C — a fixed link on a keyword trigger

```text
sync_trigger_canned_responses  type="keywords", agent_id=<agent_id>, trigger_id=<id>,
                               canned_responses=[{text:"Acá lo tenés: https://..."}]
```

This fires on the **first** trigger match in a conversation and replaces the LLM turn entirely — no
agent, no cost, no adaptation. Every later message is handled normally. Same replace-by-diff
semantics as the audios; list first, resend ids. Rejected with a 400 while the trigger's negative
config is enabled.

### D — a PDF, document or video, by follow-up

**1. Upload the file through Studio and copy its resource id from there.** The MCP upload path
(`create_resource_upload_url` / `register_creator_resource`) only accepts IMAGE and AUDIO, and
`list_creator_resources` will not show the document either — it enumerates only the agent-sendable
IMAGE/AUDIO clips. Studio's media library is the only place that id exists.

**2. Point a follow-up at it.**

```text
upsert_workflow  type="FOLLOW_UP", name="Envío de la guía", enabled=true,
                 agentIds=[<agent_id>],
                 useStaticMessage=true,
                 staticMessageContent="Te dejo la guía acá 👇",
                 staticMessageType="FILE",
                 staticMessageMetadata={ resourceId:<id>, resourceType:"DOCUMENT" }
```

Five of those are load-bearing and easy to drop. `type`, `name` and `enabled` are always required.
`agentIds` is what links the workflow to the agent — leave it off a create and the follow-up exists
but never fires. And `staticMessageContent` must carry real text: `useStaticMessage=true` on its own
is rejected, because an empty static message makes the follow-up model echo the lead's own words
back at them.

**Both type fields track the asset.** The call above sends a PDF; for a **video** it is
`staticMessageType="VIDEO"` with `resourceType:"VIDEO"`. `DOCUMENT` maps to message type `FILE`;
`IMAGE`, `VIDEO` and `AUDIO` map to themselves. The signed URL is generated fresh at send time, so
the link never expires in storage.

Timing, delays and property gating are the ordinary follow-up levers — see
`knowledge/workflows.md`.

---

## The invariant

**Three things must all be true before the agent can send an image or audio creator resource
through `send_resource`, and each one fails silently.** (The link path needs none of them.)

1. `config.resourceSending.enabled = true` on the agent, and
2. the resource's type is in `allowed_types`, and
3. the resource has `agent_usable = true` **and** a non-empty `usage_context`.

Miss the first and the agent has no `send_resource` tool *at all* — the tools are only injected
when sending is enabled with at least one allowed type. The agent will still happily say "te paso
el audio". Miss the third and the resource is simply absent from what the agent can see, while
looking correct in every listing that does not filter on it.

Read it back with `list_creator_resources agent_usable_only=true`. If the clip is not in that
list, the agent cannot send it, whatever the catalog shows.

---

## How it goes wrong

### The agent promises the file and nothing arrives

The single most common report, and it is almost never a delivery bug. Either sending was never
enabled (so the tool does not exist), or the asset is a document and no tool could ever have sent
it. Check `config.resourceSending` before reading a single line of the prompt.

### The clip is in the media library and the agent never sends it

The catalog and the agent-sendable set are not the same list. A file uploaded to the creator's
media library is just a file; it becomes sendable only once it carries **both** `agent_usable` and
a `usage_context`, and a resource missing either is dropped from `list_sendable_resources` — not
flagged, not logged, just gone. `register_creator_resource` refuses to create one half-filled, so
what you are looking at is almost always a resource uploaded with no agent metadata at all — which
is exactly what a plain Studio upload produces. Fix it with `update_creator_resource
agent_usable=true usage_context=...`. That call is pure PATCH and will happily set one field
without the other, so send both unless the resource already carries a `usage_context`.

### `[Audio: bienvenida.m4a]` appears in the transcript

Two unrelated things produce that exact string, and **only one of them is a delivery bug.** You
cannot tell them apart by the text — check whether the outbound message is a media message or a
plain-text one.

**The platform wrote it, and the audio was delivered.** Whenever a resource is sent without a
transcription or description, the transcript stores this placeholder in place of the media. Nobody
received it; it is a rendering of the conversation record. It is still worth fixing, because the
agent reads that record back as its own history, sees itself "writing" a bracketed tag, and can
start imitating the format in real messages. Fill the gap with `update_creator_resource
transcription=...` (audio) or `description=...`. Resources registered over MCP before transcription
was derived automatically are the ones missing it.

**The lead genuinely received it as a text message.** That is the real bug, and it is the prompt
emitting a media tag inline that the platform never interpreted — see the runbook.

### The audio uploads fine and never plays

MP3. It is rejected at registration, but a file renamed `.m4a` while still MP3-encoded passes the
extension check and dies at Meta.

### It works for everyone except some leads

Creator resources can carry **property conditions**. A resource gated on a custom property is
withheld from every contact who does not satisfy it, and the agent is never told why — from its
side the resource does not exist. Also check `max_per_conversation`: it counts sends of *that
specific file*, so a cap of 1 blocks a re-send of the same clip while leaving other clips free.

### Nothing sends on TikTok

TikTok needs a platform-side media id and the tool refuses outright. There is no configuration for
it. Web conversations are fine — they take a separate path that writes the message directly.

### `get_resources` returns nothing

The tool is **`get_resource`**, singular, and it takes a `topic` — it reads the `resources` block
of v5Config and returns a URL. It is unrelated to creator resources and cannot send media. If it
answers "Resource data is not configured", the deployed v5Config has no `resources` block.

---

## See also

- `knowledge/runbooks/resource-not-delivered.md` — when it *was* set up and the lead still got nothing
- `knowledge/mcp-tool-reference.md` §Creator resources — the full argument list for each tool
- `knowledge/triggers.md` — keyword, comment and ad triggers, and how a match is evaluated
- `knowledge/workflows.md` — follow-up scheduling, delays and property conditions
