> ## Documentation Index
> Fetch the complete documentation index at: https://whatsapp-rust.jlucaso.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Replies

> Create, edit, and delete WhatsApp Business quick replies, and react to changes from linked devices

Use `QuickReplies` to manage WhatsApp Business quick replies — saved `/`-triggered shortcuts that expand to a full message. You create, edit, or delete a quick reply with outbound calls. You receive changes made on a linked device (such as WhatsApp Web or the phone) as inbound events.

Quick replies sync across all linked devices via WhatsApp's app state sync mechanism (the `regular` collection, action version 2).

## Access

Access quick reply operations through the client:

```rust theme={null}
let quick_replies = client.quick_replies();
```

## Create or update a quick reply

### set\_quick\_reply

Create a new quick reply or update an existing one. Because app state is an upsert keyed by `id`, calling this with an existing `id` edits that quick reply — WhatsApp Web itself builds add and edit from the same mutation.

```rust theme={null}
pub async fn set_quick_reply(
    &self,
    id: &str,
    shortcut: &str,
    message: &str,
    keywords: Vec<String>,
    count: i32,
) -> Result<(), AppStateError>
```

**Parameters:**

* `id` — Stable identifier for the quick reply. Must be non-empty. Reuse the same `id` to edit it later.
* `shortcut` — The `/`-typed trigger text. Must be non-empty.
* `message` — The expanded message text. Must be non-empty.
* `keywords` — Extra search terms WhatsApp indexes the reply under. You can pass an empty vector.
* `count` — Usage counter. Pass `0` for a new quick reply.

**Example:**

```rust theme={null}
// Create a new quick reply
client.quick_replies()
    .set_quick_reply("5", "/hours", "We're open 9am-5pm, Mon-Fri.", vec![], 0)
    .await?;

// Edit the same quick reply later
client.quick_replies()
    .set_quick_reply("5", "/hours", "We're open 9am-6pm, Mon-Sat.", vec!["hours".into()], 12)
    .await?;
```

<Note>
  This call always sends `associatedLabelIds` empty, matching WhatsApp Web's own builder. You cannot associate a quick reply with a label through this call.
</Note>

## Delete a quick reply

### delete\_quick\_reply

Delete a quick reply.

```rust theme={null}
pub async fn delete_quick_reply(&self, id: &str) -> Result<(), AppStateError>
```

**Parameters:**

* `id` — The quick reply to delete. Must be non-empty.

**Example:**

```rust theme={null}
client.quick_replies().delete_quick_reply("5").await?;
```

<Note>
  Deletion sends the same `Set` mutation as `set_quick_reply`, with `deleted = true` and the payload fields cleared — not a syncd `Remove`. WhatsApp Web's own delete builder (`getQuickReplyDeleteMutation`) works the same way. This means the [inbound event](#quickreplyupdate) below always fires as a `QuickReplyUpdate`; check `action.deleted` to tell a delete apart from a create or edit.
</Note>

## Inbound events

### QuickReplyUpdate

You receive a quick reply change made on a linked device through the event bus. The event carries the underlying app state action, so read `action.deleted` first to tell a deletion apart from a create or edit.

```rust theme={null}
use wacore::types::events::Event;

.on_event(|event, _client| async move {
    match &*event {
        Event::QuickReplyUpdate(update) => {
            if update.action.deleted == Some(true) {
                println!("Quick reply {} deleted", update.id);
            } else {
                println!(
                    "Quick reply {} = {:?} -> {:?}",
                    update.id,
                    update.action.shortcut,
                    update.action.message,
                );
            }
        }
        _ => {}
    }
})
```

`from_full_sync` is `true` when the event came from an initial app state full sync, so you can suppress UI notifications during bootstrap.

## App state sync

| Action                               | Collection | Action version |
| ------------------------------------ | ---------- | -------------- |
| Create / update / delete quick reply | `regular`  | 2              |

<Note>
  App state sync requires the relevant encryption keys, which arrive during initial sync after pairing. Outbound quick reply calls may fail if invoked immediately after pairing before sync completes.
</Note>

<Note>
  `set_quick_reply` and `delete_quick_reply` are thin wrappers over the same app-state send path as [chat actions](/api/chat-actions#app-state-sync). They share its conflict-retry behavior, added in PR [#1158](https://github.com/oxidezap/whatsapp-rust/pull/1158). A call that loses a version race with another linked device is no longer silently dropped. The client applies the winning patches, rebuilds the mutation, and resends. It retries up to 5 times before returning `Err`. See [Chat actions — App state sync](/api/chat-actions#app-state-sync) for the full explanation.
</Note>

## Error handling

All methods return `Result<(), AppStateError>`. If you pass an empty `id`, `shortcut`, or `message` to `set_quick_reply` (or an empty `id` to `delete_quick_reply`), or a negative `count`, the call fails immediately with `AppStateError::InvalidRequest` before any network work is done.

A call can also return `AppStateError::Internal` if no app state sync key is available yet, a network error occurred, or an app-state version conflict with another device could not be resolved after 5 rebuild-and-resend attempts (see [App state sync](#app-state-sync) above).

```rust theme={null}
use whatsapp_rust::AppStateError;

if let Err(AppStateError::InvalidRequest(msg)) =
    client.quick_replies().set_quick_reply("", "/hi", "Hello!", vec![], 0).await
{
    eprintln!("Validation failed: {msg}"); // "id cannot be empty"
}
```

## Complete example

```rust theme={null}
use whatsapp_rust::Client;
use std::sync::Arc;

async fn seed_quick_replies(client: &Arc<Client>) -> anyhow::Result<()> {
    client.quick_replies()
        .set_quick_reply("greeting", "/hi", "Hi! How can I help?", vec![], 0)
        .await?;

    client.quick_replies()
        .set_quick_reply(
            "hours",
            "/hours",
            "We're open 9am-5pm, Mon-Fri.",
            vec!["hours".into(), "open".into()],
            0,
        )
        .await?;

    // Retire an old one
    client.quick_replies().delete_quick_reply("legacy-promo").await?;

    Ok(())
}
```

## See also

* [Events](/concepts/events#quickreplyupdate) — Handle `QuickReplyUpdate`
* [Chat actions](/api/chat-actions#generic-app-state-action) — The generic `send_app_state_action` escape hatch
* [Labels](/api/labels) — A similarly-shaped app-state feature with the same upsert-by-id pattern
* [State management](/advanced/state-management) — How app state sync works under the hood
