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.