Session Actions
Two write endpoints let your server add to a session the tracker already opened in the browser: a custom event, or acquisition metadata (revenue, currency, tags). Both need the signals:write scope and both need the visitor’s session id, which only the browser knows. See Session Handoff for getting it to your backend.
| Method | Path | Scope |
|---|---|---|
| POST | /v2/property/session/event | signals:write |
| POST | /v2/property/session/acquisition | signals:write |
Resolving the session id
Section titled “Resolving the session id”Both endpoints look for the session id in this order and use the first one found:
| Priority | Where | Set by |
|---|---|---|
| 1 | session_id in the JSON body | you |
| 2 | X-Snipform-Session-Id request header | signals.attachToFetch() when your frontend calls your own API, forwarded by you |
| 3 | snip_session_id body field | signals.attachToForm() hidden input, forwarded by you |
If none is present the response is 422 with error_code: "validation_failed" and the message session_id missing - provide it in the body, X-Snipform-Session-Id header, or snip_session_id form field.
POST /v2/property/session/event
| Field | Rules | Notes |
|---|---|---|
session_id | string | Optional if the header or form field carries it |
name | required, string, max 255 | The event name as it will appear in sessions, conversions and breakdowns |
value | any scalar | Stored as a string; numeric strings are also indexed as a number for event_value_num filters |
meta | object | Key/value pairs stored with the event |
curl -X POST https://api.snipform.io/v2/property/session/event \ -H "Authorization: Bearer $SNIPFORM_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "session_id": "9f1c2c1e-7b1a-4f0e-9f2b-1d2c3b4a5e6f", "name": "trial_started", "value": "pro", "meta": { "plan": "pro", "seats": 5 } }'{ "code": 200, "status": "OK", "data": { "event": { "id": "c1f0a6d2-3e4b-4c5d-8e9f-0a1b2c3d4e5f", "session_id": "9f1c2c1e-7b1a-4f0e-9f2b-1d2c3b4a5e6f", "type": "event", "name": "trial_started", "value": "pro", "meta": [{ "key": "plan", "value": "pro" }, { "key": "seats", "value": "5" }], "created_ts": 1755782400 } }}created_ts is unix seconds. The event becomes queryable on the session immediately: event_name, event_value and event_value_num in the field catalog, the Event fires trigger in Conversions, and the An event fires trigger in Automations.
Errors
Section titled “Errors”| Status | error_code | When |
|---|---|---|
| 404 | not_found | session not found for this property |
| 422 | validation_failed | name missing or over 255 characters, meta not an object, or no session id anywhere |
Acquisition
Section titled “Acquisition”POST /v2/property/session/acquisition
Records money and context on the session. Only the keys you send are touched.
| Field | Rules | Notes |
|---|---|---|
session_id | string | Optional if the header or form field carries it |
value | integer, min 0 | Revenue in minor units (cents). Recorded as an acquisition event; the session’s value is the rollup of its acquisition events |
currency_code | string, exactly 3 chars | ISO 4217, uppercased. Defaults to the session’s existing currency, else USD |
tags | array of strings, each max 255 | Merged with existing tags and de-duplicated |
cost | integer, min 0 | Accepted for backwards compatibility but no longer stamps the session. Session cost comes from cost entries and connected ad platforms; see Google Ads & Meta |
A tag in the form {source}:order:{id} (for example shopify:order:1001) is read as an order reference: the order id is stored on the acquisition event and the value is labelled as order revenue rather than a plain acquisition value.
curl -X POST https://api.snipform.io/v2/property/session/acquisition \ -H "Authorization: Bearer $SNIPFORM_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "session_id": "9f1c2c1e-7b1a-4f0e-9f2b-1d2c3b4a5e6f", "value": 12900, "currency_code": "EUR", "tags": ["plan:pro", "billing:annual"] }'{ "code": 200, "status": "OK", "data": { "session": { "id": "9f1c2c1e-7b1a-4f0e-9f2b-1d2c3b4a5e6f", "acquisition_meta": { "tags": ["plan:pro", "billing:annual"] }, "value": { "amount": 12900, "amount_usd": 14061, "currency": "EUR" } } }}amount_usd is the USD-normalised figure (also minor units) that ROAS and cross-currency comparisons use. If you send only tags, value is the session’s current value block, which may be null.
Errors
Section titled “Errors”| Status | error_code | When |
|---|---|---|
| 404 | not_found | session not found for this property |
| 422 | validation_failed | value or cost negative or non-integer, currency_code not 3 characters, a tag over 255 characters, or no session id anywhere |
End to end: an order paid webhook
Section titled “End to end: an order paid webhook”- The tracker runs on the storefront. Before checkout,
signals.bindTo('/api/snip-session')posts the session id to your backend and you store it on the cart or order. - Your payment provider calls your webhook when the order is paid.
- Your webhook handler calls
/v2/property/session/acquisitionwith the stored session id, the order total in cents, the currency, and a{shop}:order:{id}tag. - The session now carries revenue; any conversion with an Acquisition step counts it, and Automations with the A purchase is recorded trigger fire.
In PHP the same flow is two lines with the SDK: Snipform::revenue(12900, 'EUR') from a request that carries the session id, or Snipform::acquisitionFor($sessionId, [...]) from a queue job.