Skip to content

Webhook Integration

SORI API webhooks notify your systems about key events that occur inside the SORI platform. By connecting your own endpoint, you can react to campaign activity, device authentication, and administrative updates in near real-time.

Overview

SORI API provides webhook functionality to deliver structured JSON payloads as soon as notable events happen. Webhooks are configured inside the SORI Console and support the following categories:

  • Campaign events (`campaign.*`) Triggered when acampaign is recognized on a device or when the user clicks through to the action URL.
  • Authentication events (`authentication`) Fired when adevice successfully signs in through the SORI authentication flow.
  • Admin material events (`admin.material.*`) Optionalnotifications emitted when materials are created, updated, or deleted inside the console.

Near real-time notifications enable you to:

  • Instant Analytics Track and analyze campaignperformance the moment activity occurs.
  • Authentication Auditing Reconcile device logins withyour own session records.
  • Fraud Detection Detect unusual patterns as soon asthey appear.
  • Live Dashboards Power dashboards with fresh data.

Setting Up Webhooks

SORI provides two ways to configure webhook endpoints:

Global Webhook URL

  • Set in the Settings section.
  • Applies to all campaigns by default.
  • Serves as the fallback endpoint when a campaign does not override it.

Campaign-Specific Webhook URL

  • Configured in each campaign's Advanced settings.
  • Overrides the global webhook URL for that specific campaign.
  • Useful when different campaigns need dedicated handling.

Event Subscriptions (Settings → Webhooks)

In Account Settings, you can choose which event categories the account will receive:

  • Campaign events: Subscribes to campaign.*notifications (impression and click activity).
  • Authentication events: Subscribes to theauthentication event emitted during device login.
  • Administrative events: Subscribes toadmin.material.* notifications sent when console users manage materials.

New accounts default to Campaign and Authentication events. Administrators can adjust these checkboxes at any time; changes take effect immediately for future webhook deliveries.

To set up webhooks

  1. Prepare an HTTPS endpoint on your server to receive webhook events.
  2. Configure either:
    • A global webhook URL in Settings, or
    • Campaign-specific webhook URLs in individual campaign settings.
  3. Select the event categories you want to receive in Settings → Webhooks.
  4. Ensure your endpoint accepts POST requests with JSON payloads and responds quickly (within five seconds).

Event Types

Each webhook payload includes an event field containing the canonical event name. Branch new integrations on event for clarity.

Campaign Impression Event (campaign.impression)

Sent when a campaign is successfully displayed on a user's device. This event is available when Campaign events are enabled.

Campaign Click Event (campaign.click)

Sent when a user taps a campaign and is redirected to the action URL. This event is available when Campaign events are enabled.

Authentication Event (authentication)

Sent after a device completes authentication with SORI. The payload contains the query parameters that were supplied during the authentication request, which can be used to correlate the login with your own session identifiers.

Handling Webhook Events

When your endpoint receives a webhook event, it will include a JSON payload with the event details. Examples for each event type are shown below. Fields marked with null may be omitted entirely when no value is available.

Payload Schema

The following fields are delivered with every campaign webhook unless otherwise noted.

FieldTypeDescription
eventstringCanonical event name such as campaign.impression, campaign.click, or authentication.
event_typestringLegacy duplicate of event; still emitted for backward compatibility.
account_idstringIdentifier of the SORI account that owns the campaign.
created_atISO 8601 stringServer-side time when the event object was created.
timestampISO 8601 stringDelivery timestamp appended just before dispatch; useful for idempotency checks.
activity_idstringUnique identifier for the specific impression or click (campaign events only).
device_idstringDevice/session that triggered the event when available.
platformstringClient platform reported by the SDK (Android, iOS, etc.).
metadataobjectCustom key-value pairs forwarded from the SDK or authentication request.
geolocation.viewer_countrystringCountry derived from CloudFront GeoIP (returns Unknown if not detectable).
geolocation.viewer_regionstringRegion/state derived from GeoIP lookup.
geolocation.viewer_citystringCity derived from GeoIP lookup.
campaign.idstringNormalized campaign identifier guaranteed to match the console ID.
campaign.namestringCampaign display name at the time of the event.
campaign.tagsstring[]Tags assigned to the campaign (empty array when none).
campaign.imagestring (URL)HTTPS URL of the campaign thumbnail image, if configured.
material.idstringIdentifier of the material that was recognized (campaign events only).
material.namestringMaterial name; omitted when no material is associated.
material.tagsstring[]Tags stored on the material record (empty array when none).
material.imagestring (URL)Preview image URL for the material, if available.

Legacy Fields

event_type, campaign_id, campaign_name, material_id, and material_name are legacy duplicates retained only for backward compatibility and will be removed in an upcoming release. Update your integration to rely on the event field plus the nested campaign and material objects. The sample payloads below omit these deprecated keys for clarity.

For impression events:

json
{
  "event": "campaign.impression",
  "account_id": "acc_123456",
  "created_at": "2025-10-14T05:25:12.421Z",
  "activity_id": "act_7890",
  "device_id": "device_abc",
  "platform": "Android",
  "metadata": {
    "session": "abcd-1234"
  },
  "geolocation": {
    "viewer_country": "United States",
    "viewer_region": "Michigan",
    "viewer_city": "Ann Arbor"
  },
  "campaign": {
    "id": "cmp_1234",
    "name": "Fall Promotion",
    "tags": [
      "retail",
      "autumn"
    ],
    "image": "https://cdn.soriapi.com/campaigns/cmp_1234.png"
  },
  "material": {
    "id": "mat_5678",
    "name": "Audio Spot 15s",
    "tags": [
      "audio",
      "15s"
    ],
    "image": "https://cdn.soriapi.com/materials/mat_5678.png"
  },
  "timestamp": "2025-10-14T05:25:12.421Z"
}

For click events:

json
{
  "event": "campaign.click",
  "account_id": "acc_123456",
  "created_at": "2025-10-14T05:26:03.009Z",
  "activity_id": "act_7890",
  "device_id": "device_abc",
  "platform": "Android",
  "metadata": {
    "session": "abcd-1234",
    "utm_source": "push"
  },
  "geolocation": {
    "viewer_country": "United States",
    "viewer_region": "Michigan",
    "viewer_city": "Ann Arbor"
  },
  "campaign": {
    "id": "cmp_1234",
    "name": "Fall Promotion",
    "tags": [
      "retail",
      "autumn"
    ],
    "image": "https://cdn.soriapi.com/campaigns/cmp_1234.png"
  },
  "material": {
    "id": "mat_5678",
    "name": "Audio Spot 15s",
    "tags": [
      "audio",
      "15s"
    ],
    "image": "https://cdn.soriapi.com/materials/mat_5678.png"
  },
  "timestamp": "2025-10-14T05:26:03.009Z"
}

For authentication events:

json
{
  "event": "authentication",
  "account_id": "acc_123456",
  "created_at": "2025-10-14T05:15:44.832Z",
  "device_id": "device_abc",
  "platform": "Android",
  "metadata": {
    "app_version": "1.8.0",
    "session": "abcd-1234"
  },
  "geolocation": {
    "viewer_country": "United States",
    "viewer_region": "Michigan",
    "viewer_city": "Ann Arbor"
  },
  "timestamp": "2025-10-14T05:15:44.832Z"
}

Custom Metadata

The metadata field is a key-value map supplied by the SDK (for campaign events) or by the authentication request query parameters (for authentication events). It can be used to identify the user or device and to pass additional context. This field is optional. Please refer to the Metadata Provider section for more details.

Note on Location Information

Geolocation values are provided inside the geolocation object. The data is based on CloudFront GeoIP estimation and may not always be accurate. If the exact information is not available, the value Unknown is returned.

Response Requirements

Your webhook endpoint should:

  • Respond with a 2xx status code to acknowledge receipt.
  • Process events asynchronously if possible to avoid blocking.
  • Handle events idempotently (using the timestamp or activity ID).
  • Respond within 5 seconds to avoid timeouts.

Security Considerations

  • Use HTTPS for your webhook endpoint whenever possible to ensure data privacy.
  • Validate the incoming payload structure and verify the event value.
  • Keep your webhook URL confidential.
  • When app_id and secret_key are configured, verify the X-SORI-Signature header against your copy of the payload to confirm authenticity.

Error Handling

If your endpoint fails to receive events, SORI API Server will:

  • Retry failed deliveries up to 3 times.
  • Implement exponential backoff between retries.
  • Drop the event after all retries are exhausted.

Example Implementations

Python (FastAPI)

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    payload = await request.json()
    event = payload.get("event")

    if event == "campaign.impression":
        # Handle impression event
        pass
    elif event == "campaign.click":
        # Handle click event
        pass
    elif event == "authentication":
        # Handle authentication event
        pass

    return {"status": "success"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=3000)

Node.js (Express)

typescript
import express from "express";

const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
  const payload = req.body;
  const event = payload.event;

  if (event === "campaign.impression") {
    // Handle impression event
  } else if (event === "campaign.click") {
    // Handle click event
  } else if (event === "authentication") {
    // Handle authentication event
  }

  res.status(200).send({ status: "success" });
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});