> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Meta Conversions API

<!-- -->

[Server](#)[ ](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/meta)

<!-- -->

[Source code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/meta)[ ](https://www.npmjs.com/package/@walkeros/server-destination-meta)

<!-- -->

[Package](https://www.npmjs.com/package/@walkeros/server-destination-meta)

Sends walkerOS events from your server to the Meta Conversions API. Customer information such as email and phone number is normalized and SHA-256 hashed before the request leaves your server.

<!-- -->

Where this fits

Meta CAPI is a **server destination** in the walkerOS flow:

It receives events from the collector and posts each one to the Conversions API endpoint of your Meta Pixel.

## Installation[​](#installation "Direct link to Installation")

```
npm install @walkeros/server-destination-meta
```

* Integrated
* Bundled

```
import { startFlow } from '@walkeros/collector';
import { destinationMeta } from '@walkeros/server-destination-meta';

await startFlow({
  destinations: {
    meta: {
      code: destinationMeta,
      config: {
        settings: {
          accessToken: 'YOUR_ACCESS_TOKEN',
          pixelId: '1234567890',
        },
      },
    },
  },
});
```

Add to your `flow.json` destinations:

```
"destinations": {
  "meta": {
    "package": "@walkeros/server-destination-meta",
    "import": "destinationMeta",
    "config": {
      "settings": {
        "accessToken": "YOUR_ACCESS_TOKEN",
        "pixelId": "1234567890"
      }
    }
  }
}
```

[See bundled mode setup](/preview/pr-720/docs/getting-started/modes/bundled.md) | [CLI reference](/preview/pr-720/docs/apps/cli.md)

## Configuration[​](#configuration "Direct link to Configuration")

This <!-- -->destination<!-- --> uses the standard <!-- -->destination<!-- --> config wrapper (consent, data, env, id, ...). For the shared fields see [destination<!-- --> configuration](/docs/destinations#configuration). Package-specific fields live under `config.settings` and are listed below.

## Settings[​](#settings "Direct link to Settings")

| Property          | Type                               | Description                                                                                                                                                               | More |
| ----------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
| `accessToken*`    | `string`                           | Meta access token for Conversions API authentication                                                                                                                      |      |
| `pixelId*`        | `string`                           | Meta Pixel ID from your Facebook Business account                                                                                                                         |      |
| `action_source`   | `'email' \| 'website' \| 'app' \|` | Source of the event (website, app, phone\_call, etc.)                                                                                                                     |      |
| `doNotHash`       | `Array<string>`                    | user\_data keys to send without normalizing and hashing, for values that are already hashed. Only em, ph, fn, ln, db, ge, ct, st, zp, country and external\_id are hashed |      |
| `test_event_code` | `string`                           | Test event code for debugging Meta Conversions API events                                                                                                                 |      |
| `url`             | `string`                           | Custom URL for Meta Conversions API endpoint                                                                                                                              |      |
| `user_data`       | `Record<string, Mapping.Value>`    | Mapping of Meta customer information parameters to event values, applied to every event. Keys must be Meta's short names such as em and ph                                |      |

\* Required fields

## Mapping[​](#mapping "Direct link to Mapping")

This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see [mapping](/docs/mapping).

## Examples

### Form submit

A form submission is forwarded to Meta CAPI as a custom event with the event source URL.

Event

```
{
  "name": "form submit",
  "data": {
    "type": "newsletter"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "email": "user@example.com"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "c1d2e3f4a5b60002",
  "trigger": "test",
  "entity": "form",
  "action": "submit",
  "timestamp": 1700000901000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "browser",
    "platform": "web",
    "url": "https://example.com"
  }
}
```

Out

```
sendServer("https://graph.facebook.com/v22.0/1234567890/events", "{\"data\":[{\"event_name\":\"form submit\",\"event_id\":\"c1d2e3f4a5b60002\",\"event_time\":1700000901,\"action_source\":\"website\",\"user_data\":{},\"event_source_url\":\"https://example.com\"}]}", {
  "headers": {
    "Authorization": "Bearer s3cr3t"
  }
})
```

### Purchase

A completed order is sent to the Meta Conversions API as a Purchase event with value, currency, and contents.

Event

```
{
  "name": "order complete",
  "data": {
    "id": "ORD-300",
    "total": 249.99,
    "currency": "EUR"
  },
  "context": {
    "shopping": [
      "complete",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "user-123",
    "device": "device-456"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "SKU-A1",
        "price": 129.99,
        "quantity": 2
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "c1d2e3f4a5b60001",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1700000900000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "browser",
    "platform": "web",
    "url": "https://shop.example.com"
  }
}
```

Mapping

```
{
  "name": "Purchase",
  "data": {
    "map": {
      "order_id": "data.id",
      "currency": {
        "key": "data.currency",
        "value": "EUR"
      },
      "value": "data.total",
      "contents": {
        "loop": [
          "nested",
          {
            "condition": {
              "$code": "e=>\"object\"==typeof e&&null!==e&&\"entity\"in e&&\"product\"===e.entity"
            },
            "map": {
              "id": "data.id",
              "item_price": "data.price",
              "quantity": {
                "key": "data.quantity",
                "value": 1
              }
            }
          }
        ]
      },
      "num_items": {
        "fn": {
          "$code": "e=>e.nested.filter(e=>\"product\"===e.entity).length"
        }
      }
    }
  }
}
```

Out

```
sendServer("https://graph.facebook.com/v22.0/1234567890/events", "{\"data\":[{\"event_name\":\"Purchase\",\"event_id\":\"c1d2e3f4a5b60001\",\"event_time\":1700000900,\"action_source\":\"website\",\"order_id\":\"ORD-300\",\"currency\":\"EUR\",\"value\":249.99,\"contents\":[{\"id\":\"SKU-A1\",\"item_price\":129.99,\"quantity\":2}],\"num_items\":1,\"user_data\":{},\"event_source_url\":\"https://shop.example.com\"}]}", {
  "headers": {
    "Authorization": "Bearer s3cr3t"
  }
})
```

### Purchase with fbclid

A purchase is sent to Meta CAPI with an external\_id and a formatted fbc click id for ads attribution.

Event

```
{
  "name": "order complete",
  "data": {
    "id": "ORD-700",
    "total": 89.99,
    "currency": "USD"
  },
  "context": {
    "fbclid": [
      "abc123xyz",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "cust-42"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "ers",
        "name": "Everyday Ruck Snack",
        "color": "black",
        "size": "l",
        "price": 420
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "product",
      "data": {
        "id": "cc",
        "name": "Cool Cap",
        "size": "one size",
        "price": 42
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "gift",
      "data": {
        "name": "Surprise"
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "c1d2e3f4a5b60003",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1700000902000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "browser",
    "platform": "web",
    "url": "https://shop.example.com"
  }
}
```

Mapping

```
{
  "name": "Purchase",
  "data": {
    "map": {
      "currency": {
        "key": "data.currency",
        "value": "EUR"
      },
      "value": "data.total",
      "order_id": "data.id",
      "user_data": {
        "map": {
          "external_id": "user.id",
          "fbclid": "context.fbclid.0"
        }
      }
    }
  }
}
```

Out

```
sendServer("https://graph.facebook.com/v22.0/1234567890/events", "{\"data\":[{\"event_name\":\"Purchase\",\"event_id\":\"c1d2e3f4a5b60003\",\"event_time\":1700000902,\"action_source\":\"website\",\"currency\":\"USD\",\"value\":89.99,\"order_id\":\"ORD-700\",\"user_data\":{\"external_id\":\"8a3c5a67cad508582b5edf6b8352cea3ffbad7f44812c1a736b4444c0f5746aa\",\"fbc\":\"fb.1.1700000902000.abc123xyz\"},\"event_source_url\":\"https://shop.example.com\"}]}", {
  "headers": {
    "Authorization": "Bearer s3cr3t"
  }
})
```

## How it works[​](#how-it-works "Direct link to How it works")

Every event becomes one `POST` request to `{url}/{pixelId}/events`, with the access token in an `Authorization: Bearer` header. The default `url` is `https://graph.facebook.com/v22.0`, and a trailing slash is optional. The destination does not batch events.

The server event is built from the walkerOS event:

| Field              | Value                                                                                                                                 |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `event_name`       | The walkerOS event name, such as `order complete`. Set `name` in a mapping rule to send a Meta event name such as `Purchase`.         |
| `event_id`         | The walkerOS `event.id`. If the Meta Pixel tracks the same event in the browser, give it the same ID so Meta can deduplicate the two. |
| `event_time`       | The event timestamp converted from milliseconds to seconds.                                                                           |
| `action_source`    | `settings.action_source`, or `website` if it is not set.                                                                              |
| `event_source_url` | `event.source.url`, added only when `action_source` is `website`.                                                                     |

Keys returned by `data` mappings are added to the top level of the server event. `user_data` combines `settings.user_data` with the `user_data` mapped in the destination `config.data` and in the rule's `data`. When the same key is mapped in more than one place, the rule's value wins.

If `user_data` contains `fbclid`, the destination replaces it with `fbc` in the format `fb.1.{creationTime}.{fbclid}`. The creation time is the session start in milliseconds when the collector has a session, and the event timestamp otherwise.

If `test_event_code` is set, it is added to the request body.

A response outside the 2xx range, a network error, or a timeout makes the push throw with the response details. The destination does not retry.

### Customer information hashing[​](#customer-information-hashing "Direct link to Customer information hashing")

`user_data` keys must be Meta's parameter names from [Meta's customer information parameters](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters), such as `em` for email and `ph` for phone. `fbclid` is also accepted and becomes `fbc`. The settings schema and the TypeScript types list only these parameter names, so a key like `email` is rejected by this package's schema and by type checking. Any other key that reaches `user_data`, for example through a `data` mapping, is dropped before the request is sent, with a warning that lists the key names but never their values.

The destination normalizes and SHA-256 hashes these keys. Arrays are handled item by item.

| Key           | Normalization before hashing                                                                                                                                                                                                                             |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `em`          | Trim and lowercase.                                                                                                                                                                                                                                      |
| `ph`          | Keep only digits and drop leading zeros. The destination cannot add a country code, so the mapped value must include one.                                                                                                                                |
| `fn`, `ln`    | Lowercase, remove punctuation, and trim. Accented and non-Latin letters are kept.                                                                                                                                                                        |
| `db`          | Only year-first dates are normalized: `1997-02-16` becomes `19970216`. Other formats such as `02/16/1997` are only trimmed and cannot match, so map dates as `YYYYMMDD`. The destination logs a warning when a date is not 8 digits after normalization. |
| `ge`          | First letter, lowercased (`Female` becomes `f`).                                                                                                                                                                                                         |
| `ct`, `st`    | Lowercase and remove everything except letters and digits. Full state names are not converted to codes.                                                                                                                                                  |
| `zp`          | Lowercase and remove spaces and dashes. U.S. ZIP+4 codes such as `94035-1234` or `940351234` keep the first 5 digits.                                                                                                                                    |
| `country`     | Trim and lowercase. Map the ISO 3166-1 alpha-2 code, since country names are not converted.                                                                                                                                                              |
| `external_id` | None. The value is hashed as it is.                                                                                                                                                                                                                      |

The other parameters, such as `client_ip_address`, `client_user_agent`, `fbc`, or `fbp`, are sent unchanged. If a value is already hashed, add its key to `doNotHash` and the destination sends it exactly as mapped.
