LiveRecoverVYG Developer Docs
Brand Data API

Managing your integration

Create a provider, point it at your events, and register an event definition — end to end over the API.

This walks through setting up an integration entirely over the API: create a provider, tell it how to read your events, and register a definition describing one of them.

Everything here can also be done from the LiveRecover dashboard. The API exists so you can do it from your own code — during onboarding, across environments, or whenever clicking through Settings is not where this work belongs.

You will need a Brand Data API key.

The model, briefly

  • A provider is a source of events. It owns an inbound webhook URL and a signing secret. Most brands have one.
  • An event definition describes a single kind of event that provider sends — its name, the shape of its payload, and how to find the customer inside it.
  • A type matcher on the provider decides which definition an inbound webhook resolves to.

1. Create a provider

curl -sS -X POST https://<your-vyg-api-host>/providers \
  -H "Authorization: Bearer $LIVERECOVER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "My Store" }'
{
  "data": {
    "id": "3f0f8a1e-...",
    "key": "my_store",
    "name": "My Store",
    "lookupId": "ezLUu3AC28uEa3qb4tTE4",
    "isEnabled": true,
    "settings": {},
    "apiKey": "vyg_live_...",
    "webhookSecret": "whsec_...",
    "webhookUrl": "https://<hook-host>/custom/ezLUu3AC28uEa3qb4tTE4"
  }
}

apiKey and webhookSecret are returned exactly once, in this response. They are not stored in a form we can show you again. Save them now.

webhookUrl is where you will post your events, signed with webhookSecret.

If you supply your own key and it is already taken, you get a 409 rather than a silently-renamed provider — otherwise you could end up pointing your webhooks at a provider you did not create. Omit key and we derive one from name and make it unique for you.

2. Register an event definition

Say your platform sends an order like this:

{
	"type": "order.created",
	"id": "ord_1029",
	"email": "ada@example.com",
	"created_at": "2026-07-14T12:00:00Z",
	"total": 84.5
}

Describe it:

curl -sS -X POST https://<your-vyg-api-host>/providers/3f0f8a1e-.../event-definitions \
  -H "Authorization: Bearer $LIVERECOVER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "new_order",
    "name": "New Order",
    "payloadSchema": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "email": { "type": "string" },
        "created_at": { "type": "string" },
        "total": { "type": "number" }
      },
      "required": ["id", "email"]
    },
    "identifierMappings": {
      "external_id_path": ["id"],
      "email_path": ["email"],
      "received_at_path": ["created_at"]
    }
  }'

Two things are worth understanding here.

payloadSchema is JSON Schema, and it is checked with the same compiler that validates your live events. A schema that saves is a schema that will validate.

identifierMappings is how we find the customer. external_id_path is required, and you must give at least one of email_path or phone_path — without a contact channel there is nobody to reach, and the event would arrive and do nothing. Each is a list of candidate paths in dot notation (id, customer.email, data.items.0.sku) resolved against your event payload — not JSONPath, so no leading $.. The first path that resolves to a non-empty value wins.

3. Point your events at the definition

Your payload carries "type": "order.created", but the definition is called new_order. A type matcher connects them. The same settings update also tells us where the event's idempotency id lives — a request mapping:

curl -sS -X PATCH https://<your-vyg-api-host>/providers/3f0f8a1e-... \
  -H "Authorization: Bearer $LIVERECOVER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "type_matchers": {
        "new_order": { "from": "body", "key": "type", "equals": "order.created" }
      },
      "request_mapping": {
        "event_id": { "from": "body", "path": "id" }
      }
    }
  }'

Read the matcher as: an event whose body field type equals order.created is a new_order.

The request_mapping.event_id is required for a flat payload like this one. By default we expect the envelope { "type": ..., "event_id": ..., "data": {...} } and read the id from event_id — a post without one is rejected. Your payload puts the id at the root as id, so the mapping points there (from: "body", path: "id"). Leaving payload unset means the whole body is your payload, which is why the identifierMappings paths above (id, email, …) resolve against the root.

settings is merged, not replaced — sending only type_matchers will not drop your request_mapping, and vice versa.

4. Send an event

Post your payload to the provider's webhookUrl, signed with its webhookSecret. See the Custom Integration guide for the signing details.

Renaming an event type later

If you rename a definition's type, we re-point both of the things that depend on it: any workflows bound to the old name, and the provider's type matcher.

This matters. The matcher is what routes your inbound webhook to the definition — if it kept pointing at the old name, your events would stop matching, you would get a 200 on the rename, and nothing would tell you that ingestion had stopped. So the rename moves both, or it fails and rolls back. It never half-succeeds quietly.

Changing a payload schema

schemaVersion increments whenever payloadSchema changes, and it guards against concurrent edits. Pass the version you read:

{ "payloadSchema": { "...": "..." }, "schemaVersion": 3 }

If the definition changed underneath you, you get a 409 instead of a silent overwrite. Re-read it, re-apply your change, and send again. Without this, a lost update would leave us validating your new events against the old schema — and you would have no way of knowing.

On this page