> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create and attach an automation

> Create a deposit-triggered swap automation and enable it for a wallet.

This guide creates an automation that converts USDC and USDT deposits into pathUSD on Tempo. The app first creates a reusable automation definition, then attaches it to a source wallet.

<Warning>
  Run these examples from a trusted server. Never expose the Privy app secret in client-side code.
</Warning>

## Prerequisites

The integration requires:

* A Privy app ID and app secret.
* A source [Privy wallet](/wallets/overview).
* An authorization signature when the source wallet has an [owner](/controls/authorization-keys/owners/overview).
* [Gas sponsorship](/wallets/gas-and-asset-management/gas/overview).

## 1. Create the automation

Create an app-scoped automation with `POST /v1/wallet_automations`.

See [configure triggers and actions](/controls/automations/configuration) for the complete set of trigger, filter and action semantics we support today.

The source asset aliases below do not specify a chain. Privy expands each asset to its supported deployments. The destination includes a chain because every swap must have one destination.

<Tabs>
  <Tab title="cURL">
    ```bash theme={"system"}
    curl --request POST 'https://api.privy.io/v1/wallet_automations' \
      --user "$PRIVY_APP_ID:$PRIVY_APP_SECRET" \
      --header "privy-app-id: $PRIVY_APP_ID" \
      --header 'content-type: application/json' \
      --data '{
        "name": "Normalize deposits to Tempo PathUSD",
        "owner_id": null,
        "config": {
          "trigger": {
            "type": "deposit",
            "assets": {
              "mode": "include",
              "values": [
                {"asset": "usdc"},
                {"asset": "usdt"}
              ]
            }
          },
          "action": {
            "type": "swap",
            "destination_chain_asset": {
              "asset": "pathusd",
              "chain": "tempo"
            }
          }
        }
      }'
    ```
  </Tab>

  <Tab title="Node SDK">
    ```ts {skip-check} theme={"system"}
    import {PrivyClient} from '@privy-io/node';

    const privy = new PrivyClient({
      appId: process.env.PRIVY_APP_ID!,
      appSecret: process.env.PRIVY_APP_SECRET!
    });

    const automation = await privy.walletAutomations().create({
      name: 'Normalize deposits to Tempo PathUSD',
      owner_id: null,
      config: {
        trigger: {
          type: 'deposit',
          assets: {
            mode: 'include',
            values: [{asset: 'usdc'}, {asset: 'usdt'}]
          }
        },
        action: {
          type: 'swap',
          destination_chain_asset: {asset: 'pathusd', chain: 'tempo'}
        }
      }
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os
    import requests

    app_id = os.environ["PRIVY_APP_ID"]
    app_secret = os.environ["PRIVY_APP_SECRET"]

    response = requests.post(
        "https://api.privy.io/v1/wallet_automations",
        auth=(app_id, app_secret),
        headers={"privy-app-id": app_id},
        json={
            "name": "Normalize deposits to Tempo PathUSD",
            "owner_id": None,
            "config": {
                "trigger": {
                    "type": "deposit",
                    "assets": {
                        "mode": "include",
                        "values": [{"asset": "usdc"}, {"asset": "usdt"}],
                    },
                },
                "action": {
                    "type": "swap",
                    "destination_chain_asset": {"asset": "pathusd", "chain": "tempo"},
                },
            },
        },
    )
    response.raise_for_status()
    automation = response.json()
    ```
  </Tab>
</Tabs>

Save the returned `id`. API responses contain the resolved `asset_address` and `caip2` values instead of input aliases.

See the [create automation API reference](/api-reference/wallet-automations/create) for the complete schema.

## 2. Attach it to a wallet

An attachment enables the automation for one wallet. In the swap example, the params indicate that its `destination_address` receives the output of every generated swap.

<Tabs>
  <Tab title="cURL">
    ```bash theme={"system"}
    curl --request POST \
      "https://api.privy.io/v1/wallets/$WALLET_ID/automations/attach" \
      --user "$PRIVY_APP_ID:$PRIVY_APP_SECRET" \
      --header "privy-app-id: $PRIVY_APP_ID" \
      --header 'content-type: application/json' \
      --header "privy-authorization-signature: $PRIVY_AUTHORIZATION_SIGNATURE" \
      --data "{
        \"automation_ids\": [\"$AUTOMATION_ID\"],
        \"params\": {
          \"destination_address\": \"$DESTINATION_WALLET_ADDRESS\"
        }
      }"
    ```
  </Tab>

  <Tab title="Node SDK">
    ```ts {skip-check} theme={"system"}
    const {data: attachments} = await privy.wallets().attachAutomations(process.env.WALLET_ID!, {
      automation_ids: [automation.id],
      params: {
        destination_address: process.env.DESTINATION_WALLET_ADDRESS!
      },
      authorization_context: {
        signatures: [process.env.PRIVY_AUTHORIZATION_SIGNATURE!]
      }
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os
    import requests

    app_id = os.environ["PRIVY_APP_ID"]

    response = requests.post(
        f"https://api.privy.io/v1/wallets/{os.environ['WALLET_ID']}/automations/attach",
        auth=(app_id, os.environ["PRIVY_APP_SECRET"]),
        headers={
            "privy-app-id": app_id,
            "privy-authorization-signature": os.environ[
                "PRIVY_AUTHORIZATION_SIGNATURE"
            ],
        },
        json={
            "automation_ids": [automation["id"]],
            "params": {
                "destination_address": os.environ["DESTINATION_WALLET_ADDRESS"]
            },
        },
    )
    response.raise_for_status()
    attachments = response.json()["data"]
    ```
  </Tab>
</Tabs>

The authorization signature must cover this exact request. Use Privy's [authorization-signature utilities](/controls/authorization-keys/using-owners/sign/utility-functions) to produce it. An ownerless wallet does not require a wallet-owner signature.

<Info>
  Attaching an automation does not make it a general-purpose wallet signer. Privy authorizes each
  execution against the attachment's trigger, action, wallet, and destination.
</Info>

## 3. Send a matching deposit

Send USDC or USDT to the source wallet on a supported chain. Privy detects the deposit and evaluates enabled attachments in creation order. The first matching automation creates a swap wallet action.

Privy reads the current asset balance when the execution runs. **Earlier funds held by the wallet can therefore be included in the swap.**

## 4. Observe the execution

Subscribe to [`wallet_automation.submitted`](/controls/automations/webhooks) to receive both the automation execution ID and the generated wallet action ID.

The app can also list executions for the source wallet:

<Tabs>
  <Tab title="cURL">
    ```bash theme={"system"}
    curl --request GET "https://api.privy.io/v1/wallet_automations/executions?wallet_id=$WALLET_ID" \
      --user "$PRIVY_APP_ID:$PRIVY_APP_SECRET" \
      --header "privy-app-id: $PRIVY_APP_ID"
    ```
  </Tab>

  <Tab title="Node SDK">
    ```ts {skip-check} theme={"system"}
    const executions = [];

    for await (const execution of privy.walletAutomations().listExecutions({
      wallet_id: process.env.WALLET_ID!
    })) {
      executions.push(execution);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os
    import requests

    app_id = os.environ["PRIVY_APP_ID"]
    params = {"wallet_id": os.environ["WALLET_ID"], "limit": 100}
    executions = []

    while True:
        response = requests.get(
            "https://api.privy.io/v1/wallet_automations/executions",
            auth=(app_id, os.environ["PRIVY_APP_SECRET"]),
            headers={"privy-app-id": app_id},
            params=params,
        )
        response.raise_for_status()
        page = response.json()
        executions.extend(page["data"])

        if not page["next_cursor"]:
            break
        params["cursor"] = page["next_cursor"]
    ```
  </Tab>
</Tabs>

Use the execution's `wallet_action_id` to track the final swap through the [wallet action lifecycle](/wallets/actions/lifecycle).
