---
title: "Create custom conversion"
method: POST
path: "/v1/accounts/{accountId}/custom-conversions"
tags: ["Ad Accounts"]
---

# Create custom conversion

`POST /v1/accounts/{accountId}/custom-conversions`

Provision the Meta custom conversion an ads flow optimises toward, and hand back the
`customConversionId` for `promotedObject.customConversionId` on POST /v1/ads/create.
Removes the manual "create it in Ads Manager first" step.

**Reuse is ours, not Meta's.** Meta's create is not idempotent, so a retried request
would otherwise mint a duplicate carrying none of the original's optimisation history.
A non-archived conversion with the same `name` on the same `pixelId` is returned
instead of created, with `reused: true` and a 200 rather than a 201.

`rule` is forwarded verbatim in Meta's own grammar (e.g.
`{"url": {"i_contains": "thank-you"}}`); Meta validates it and rejects a malformed one
with "A conversion rule is required at creation time".

## Path parameters

- `accountId` string, required

## Request body

- object
  - `adAccountId` string, required — Platform ad account id (Meta act_<n>, Google customer id, LinkedIn account id, ...).
  - `name` string, required — Also the reuse key, together with pixelId.
  - `pixelId` string, required — Meta pixel id (event_source_id). From GET /v1/accounts/{accountId}/tracking-tags.
  - `customEventType` string, required — Meta custom_event_type, e.g. LEAD, PURCHASE, OTHER.
  - `rule` object, required — Meta conversion rule, forwarded verbatim.

## Response `200`

An existing custom conversion was reused

- CustomConversionResult
  - `adAccountId` string
  - `customConversionId` string — Drops straight into promotedObject.customConversionId on POST /v1/ads/create.
  - `reused` boolean — True when an existing conversion matched name + pixelId; the response is then a 200.
  - `customConversion` CustomConversion
    - `id` string
    - `name` string, nullable
    - `rule` object, nullable — Meta's rule, parsed back from the string Meta stores.
    - `customEventType` string, nullable
    - `pixelId` string, nullable — Meta's event_source_id, the pixel the rule reads from.
    - `isArchived` boolean

## Other responses

- `201` — Custom conversion created
- `400` — Invalid input, or Meta rejected the conversion (bad rule, per-account cap reached)
- `401` — Unauthorized
- `403` — Ads access required, or the token lacks the ads permissions.
- `404` — The account or requested resource was not found or is not accessible. An account ID may have been disconnected and removed. Read GET /v1/accounts for current account IDs.
- `409` — The account exists but is inactive or needs reconnection. Reconnect it, then read GET /v1/accounts for its current account ID before retrying. Code: ads_connection_required.

## Changes

- **2026-09-16** `3e6ddf2a99ea` — 2 info
  - added the optional property `details/budgetScope` to the response with the `404` status
  - added the optional property `details/budgetScope` to the response with the `409` status
- **2026-09-15** `0dba7d004d75` — 4 info
  - added the optional property `details/quotaExhausted` to the response with the `404` status
  - added the optional property `details/quotaExhausted` to the response with the `409` status
  - added the optional property `details/quotaScope` to the response with the `404` status
  - added the optional property `details/quotaScope` to the response with the `409` status
- **2026-09-10** `e70ed06e7150` — 2 info
  - added the non-success response with the status `404`
  - added the non-success response with the status `409`
- **2026-08-10** `1e9a379eafca` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/accounts/:accountId/custom-conversions/post.md)

---

[API](https://skmtc.dev/zernio/apis/zernio-api.md) · [All operations](https://skmtc.dev/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zernio/apis/zernio-api/revisions/eded32b62823?raw)
