Send app events
POST/v1/app_events
Accepts up to 100 events in one request. With a member token, every event belongs to that member. With a secret key (from your server), each event names its guest with member_id or anonymous_id. Events are deduplicated by event_id, so resending a batch after a timeout is safe. They reach unPLUG within about 20 minutes.
Permissions: Member token, or secret key with members:write.
Guide
App eventsRequest headers#
Idempotency-KeystringAny string up to 255 characters; use a UUID v4. Kept 24 hours per key (or member) and endpoint. Same key and body replays the first response withIdempotent-Replayed: true.Max Length 255
Request body#
- 1 to 100 events.Min Items 1Max Items 100
Show attributesAppEvent
event_idstring (uuid)RequiredCreated once when the event happens and reused on every retry.namestringRequiredsnake_case, up to 64 characters. Recommended:app_opened,session_started,screen_viewed,product_viewed,added_to_cart,removed_from_cart,checkout_started,signed_up,signed_in,push_permission_changed,reward_viewed.occurred_atstring (date-time)RequiredWhen it happened on the device.member_idstring (uuid)Secret key only. The member, if the guest is signed in. Ignored with a member token.anonymous_idstringYour id for the install, created before sign-in. With a secret key, sendmember_id,anonymous_idor both.device_idstringRequiredThe same id you use for device registration. For a website, a per-browser id.session_idstringRequiredYour id for the current session.propertiesobjectFlat key and value pairs: strings, numbers or booleans. No personal data: no names, email addresses, phone numbers, street addresses or text the guest typed.
accepted counts new events. duplicates counts events whose event_id was already received; they are dropped.
Response fields#
Returns 202 with AppEventResult
acceptedintegerduplicatesinteger
From your server#
curl -X POST https://api.unplugdining.com/v1/app_events \
-H "Authorization: Bearer $UNPLUG_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"event_id": "b6e0d4a8-3c1f-4a72-9e5b-8d2f7c0a1e93",
"name": "screen_viewed",
"occurred_at": "2026-11-03T14:08:12Z",
"anonymous_id": "c5f1a8e3-7b2d-4c96-91e4-0a6d3f8b2c71",
"device_id": "9c7e2f14-58ab-4d3e-b1a0-6f2d8e47c935",
"session_id": "1d8e4b2f-6a3c-4e91-b7d5-2f0c9a8e6b34",
"properties": { "screen": "menu" }
}
]
}'const response = await fetch("https://api.unplugdining.com/v1/app_events", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UNPLUG_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
events: [
{
event_id: "b6e0d4a8-3c1f-4a72-9e5b-8d2f7c0a1e93",
name: "screen_viewed",
occurred_at: "2026-11-03T14:08:12Z",
anonymous_id: "c5f1a8e3-7b2d-4c96-91e4-0a6d3f8b2c71",
device_id: "9c7e2f14-58ab-4d3e-b1a0-6f2d8e47c935",
session_id: "1d8e4b2f-6a3c-4e91-b7d5-2f0c9a8e6b34",
properties: { screen: "menu" },
},
],
}),
});
const data = await response.json();import os
import requests
response = requests.post(
"https://api.unplugdining.com/v1/app_events",
headers={
"Authorization": f"Bearer {os.environ['UNPLUG_KEY']}",
},
json={
"events": [
{
"event_id": "b6e0d4a8-3c1f-4a72-9e5b-8d2f7c0a1e93",
"name": "screen_viewed",
"occurred_at": "2026-11-03T14:08:12Z",
"anonymous_id": "c5f1a8e3-7b2d-4c96-91e4-0a6d3f8b2c71",
"device_id": "9c7e2f14-58ab-4d3e-b1a0-6f2d8e47c935",
"session_id": "1d8e4b2f-6a3c-4e91-b7d5-2f0c9a8e6b34",
"properties": {"screen": "menu"},
},
],
},
)
data = response.json()Error descriptions#
A field is the wrong type, a batch has more than 100 events, or properties isn't flat. param names the event, for example events[1].occurred_at. The whole batch is refused; drop or fix that event and resend.
A required field is missing, or a secret-key event has neither member_id nor anonymous_id.
Secret key only, a member_id that doesn't exist.
Get a new member token and resend the batch.
Wait Retry-After seconds and resend. Batching keeps you well under the limit.
Any endpoint can also return the common errors.