Skip to main content
An event source is where an advertiser’s conversion events come from: a website, an app or a server. You register it once, with the event types it sends and the domains it may send from, then log events to it from your server. Semicola rolls the events up per day and event type and reports the source’s health.
Event sources count conversions and report health. A campaign can name a source in an event optimization goal: when that campaign buys on a seller, Semicola registers the source on the seller’s account and forwards later events to it. Conversions don’t feed Semicola’s own reporting or attribution.

In the app

Open Event sources for an advertiser (or ask Semi, which calls open_page with page: "event_sources"). Add event source asks for: Selecting a source shows Log an event from your server, a server-side snippet for that source, and Send test event, which sends one event as test traffic (testEventCode): it’s validated like any event but not counted, so the source’s health and counts don’t change.

Event types

Health

Each source also reports lastEventAt, eventsLast24h and eventsLast7d.

How a source reaches each seller

Register a source once, on the advertiser, never per seller. When a campaign whose optimization goal names the source sends a buy to a seller, Semicola registers the source on that seller’s account with AdCP sync_event_sources, as part of sending the buy. That step waits at most 5 seconds (shared with forwarding the campaign’s audiences) and never holds the buy back. The seller answers with its own id for the source, and every later buy with that seller sends the seller’s id in the goal, not yours. If the seller hasn’t returned an id, the buy goes ahead without that one goal: Semicola never sends a source id the seller was never given. The campaign’s other goals still go. Each source lists where it has been registered in sellerProvisioning, one entry per seller account: An entry also carries storefrontId (the seller), sellerAccountId (the seller account the source was registered on: the mapped ads account for a platform seller, otherwise the advertiser’s brand domain) and lastSyncedAt. Every source is managedBy: "buyer" with sellerId: null. Turning Events off for a seller in Connections stops all of this for that seller: no registration, no forwarded events, and event goals are left out of its buys.

From an agent

Over MCP, find sources with search (kind: "measurement_source", filter.advertiserId, and a query matched against the name, eventSourceId and integration platform) and read one with get, passing its id and the advertiserId. The id is event: followed by the eventSourceId; campaign goals use the plain eventSourceId. Archived sources aren’t listed. Create or update one with save_measurement_source (sourceType: "event"), and log events with log_event (the same fields as the REST call below). An agent can also send the person to the Event sources page. See the Set up an event source skill.

REST

All paths are under https://api.semicola.com/api/v2/buyer.

Create or update a source

The response is { "created": true, "source": { … } }; created is false when the key already existed. integrationPlatform is an optional free-text label.

Sync several sources

Each entry reports action: created, updated or failed (with an error, for example when event_types lists no supported type). delete_missing isn’t supported and is refused: a sync never deletes sources.

Log events

rejected lists each refused event by index with a reason, for example “purchase is not enabled on this source.” Logging to a source key that doesn’t exist returns NOT_FOUND. Use eventId for idempotency. An event whose eventId was already logged for the same event type on the same source, earlier in the batch or in an earlier call, is dropped and counted in duplicates instead of accepted. Events without an eventId are always counted, so a resent one counts twice. A rejected event (a type that isn’t enabled, or a time more than an hour ahead) doesn’t use up its id. log_event over MCP behaves the same way. Test traffic. Add testEventCode to the call to mark it as test traffic: its events are validated and reported in accepted, rejected and duplicates like any others, but they aren’t counted in the source’s totals or summaries, don’t change its health or last event, and don’t use up their eventIds. Sellers receive them with test_event_code, so they validate them without counting them either. Custom events. An event with eventType: "custom" needs a customEventName; it’s forwarded to sellers as custom_event_name. Forwarding to sellers. Accepted events that carry an eventId are forwarded with AdCP log_event to every seller the source is provisioned on whose Events switch is on (unless you’ve turned the source’s syndication to that seller off), under the seller’s own id for the source; value and currency travel as custom_data. Events without an eventId are counted here but never forwarded, because sellers deduplicate on it. Duplicates and rejected events aren’t forwarded. Every seller is sent the batch at once, and the log call waits at most 5 seconds for all of them together; it doesn’t report each seller’s result. A seller that doesn’t answer in time misses that batch (resending it is safe: sellers deduplicate on eventId). What Semicola keeps is a daily count and total value, and an hourly count, per event type, plus the event ids it has seen. It doesn’t store individual events or any identity fields, so the log call takes no user-match data (hashed emails, click ids, device ids).

Hourly summary

GET /api/v2/buyer/advertisers/{advertiserId}/events/summary returns hourly counts for the advertiser’s sources: optional eventType, startHour and endHour (ISO 8601; hours are whole UTC hours, end exclusive). Without a window it covers the last 24 hours.
Hourly counts start with events logged after this summary was added; older events exist only as daily counts. Not available yet: per-event custom_data, match quality, seller-managed sources, and pre-aggregated measurement data uploads.