Use Atune with Segment
Route Segment track, identify, and group messages into Atune without changing your downstream understanding model.
Atune is native-first. Use the SDK when you can install it directly.
If Segment is already your event source of truth, send Segment messages to Atune instead. Segment is an ingress adapter. After mapping, events and identity updates enter the same Atune ingest core as native capture.
Endpoint
Send single messages or batch envelopes to:
https://test.useatune.com/api/v1/segmentUse your workspace API key as a Bearer token.
Connect from Segment
Until Atune is listed in Segment's catalog, use Segment's generic Webhooks (Actions) destination. It forwards the raw Segment message, which is what this endpoint accepts. The same steps, with your real endpoint and key filled in, are on the Install page in the Atune app.
- In Segment, open Connections → Destinations → Add destination and pick Webhooks (Actions). Connect it to the source that carries your product events.
- Add a mapping with the Send action. Set the trigger to Event Type is Track, Identify or Group. Atune rejects
page,screen, andaliascalls. - Set URL to
{{ATUNE_URL}}/api/v1/segmentand Method toPOST. - Under Headers, add
Authorizationwith the valueBearer <YOUR_API_KEY>, using your key from Settings → API key. - Leave Data as the default (the entire event). Batching is optional: Atune accepts single messages,
{ batch: [...] }envelopes, and the bare arrays this destination sends when batching is on. If you turn batching on, set Batch Size to 500 or lower (see Batch limits). - Save and enable the destination, then send one track call and check it on the Install page or in Settings → Events, filtered to Segment.
You do not need to add a group id to your calls. Atune scores accounts, and it works out the account for each message (see Messages without an account).
Track
track creates an Atune event. When the message names an account, Atune uses this precedence:
- top-level
accountId context.groupIdcontext.traits.accountId(oraccount_id)context.traits.companyId(orcompany_id)properties.accountId(oraccount_id)properties.companyId(orcompany_id)properties.groupId(orgroup_id)context.traits.company.idorproperties.company.id(Segment's reservedcompanyobject)
identify reads the same fields from its traits, so an identify with traits.company.id places the user in that account.
IDs can be strings or numbers. 14042 and "14042" name the same account.
If none of those fields exist, the event goes to the user's current account (see below). A track with neither a userId nor an account field is rejected.
curl -X POST https://test.useatune.com/api/v1/segment \-H "Authorization: Bearer <YOUR_API_KEY>" \-H "Content-Type: application/json" \-d '{ "type": "track", "messageId": "seg-msg-001", "event": "Invoice Sent", "userId": "user_123", "properties": { "accountId": "acme", "amount": 1200 }}'Sign in to find your API key in Settings.
Repeated track deliveries with the same messageId are idempotent per workspace.
Identify
identify can attach name and email to a user. It is treated as personal data and requires the workspace DPA to be accepted. The account is optional. Without one, the traits are applied to every account the user belongs to.
curl -X POST https://test.useatune.com/api/v1/segment \-H "Authorization: Bearer <YOUR_API_KEY>" \-H "Content-Type: application/json" \-d '{ "type": "identify", "userId": "user_123", "traits": { "name": "Kari Nordmann", "email": "kari@acme.example" }}'Sign in to find your API key in Settings.
Messages without an account
Segment's usual flow is identify first, then group, and track calls without a group id. Atune accepts this flow as it is. A message without an account is resolved from its userId:
- The user belongs to one or more accounts.
identifytraits are applied to every account the user belongs to.trackevents go to the account the user was most recently grouped into, so the lastgroupcall wins. This covers users who belong to several accounts. - The user belongs to no account yet.
identifycreates no account. Atune keeps the user's name and email and applies them when agroupcall or atrackwith a group id places the user in an account. Atrackwithout any account becomes an account of one, with theuserIdas account ID, named after the user. This is how B2C products without accounts work in Atune.
A user who later joins an account. Segment's mobile SDKs (analytics-swift, analytics-kotlin) do not add context.groupId to track calls. Mobile apps often call group only at login. A user's first mobile events can then reach Atune before their group call and land on an account of one. When that user is later placed in a real account, Atune moves those events to the account and removes the account of one. You do not need to change your instrumentation. An account of one that reported a paid subscription (accountStage: paying or an MRR) is a B2C customer and stays as it is. Until then, such a person is listed under Users without a company, not as an account.
To send an event to a specific account instead, set context.groupId on that track call.
Group
group attaches company identity to an account. groupId becomes the Atune account ID. traits.company or traits.name becomes the account display name.
curl -X POST https://test.useatune.com/api/v1/segment \-H "Authorization: Bearer <YOUR_API_KEY>" \-H "Content-Type: application/json" \-d '{ "type": "group", "groupId": "acme", "userId": "user_123", "traits": { "name": "Acme AS", "plan": "Pro", "seats": 12 }}'Sign in to find your API key in Settings.
Plan and lifecycle
Atune reads the account's plan, seats, stage, trial end and MRR from group and identify traits. Send them on either call, or on both.
| Field | Traits Atune reads | Value |
|---|---|---|
| Plan | plan, accountPlan, or company.plan | The plan name in your product, for example Pro. At most 100 characters. |
| Seats | seats or accountSeats | A positive whole number. |
| Stage | accountStage or stage | trial or paying, in any letter case. |
| Trial end | trialEndsAt | An ISO 8601 date. |
| MRR | mrr or accountMrr | What the account pays per month, in major units (49.5 = 49.50). 0 is kept. |
| Currency | currency or accountCurrency | ISO 4217 code for MRR, for example NOK. |
Atune keeps a history of these values, so a paying account that lowers its MRR or seats shows up as Downgraded in the Inbox. See Trial, paying and subscription value.
On identify, the values go to the account the message resolves to. That is the account named in the message, or the user's current account if none is named (see Messages without an account). Invalid values are ignored, and the message is still accepted.
Batch
Segment-style batch envelopes are supported, and so are bare arrays of messages (what Webhooks (Actions) sends with batching on). If sentAt is present, Atune uses it as the timestamp fallback for track messages that do not include timestamp.
curl -X POST https://test.useatune.com/api/v1/segment \-H "Authorization: Bearer <YOUR_API_KEY>" \-H "Content-Type: application/json" \-d '{ "sentAt": "2026-07-26T10:00:00.000Z", "batch": [ { "type": "track", "messageId": "seg-msg-002", "event": "Report Generated", "properties": { "accountId": "acme" } }, { "type": "group", "groupId": "acme", "traits": { "name": "Acme AS" } } ]}'Sign in to find your API key in Settings.
An invalid message does not reject the batch. Atune stores the valid messages and lists the invalid ones in rejected, each with its index and error. The response is 200, so Segment does not retry the whole batch. A batch with no valid messages returns 400.
If a batch contains identify or group, the whole batch requires the workspace DPA. This prevents partial identity writes.
Batch limits
| Limit | Value | Over the limit |
|---|---|---|
| Batch request body | 512 KB | 413, nothing stored |
| Messages per batch | 1000 | 413, nothing stored |
| One message, alone or inside a batch | 16 KB | Alone: 413. Inside a batch: listed in rejected, the rest is stored |
| Messages per workspace | 2,000 events per 10 seconds by default, and separately 2,000 identify/group writes (accounts and users touched) | 429 with Retry-After: 10, nothing from the request is stored |
Every message in a batch counts toward the rate limit. A full batch of 1000 fits, but Segment can send several batches at once, so with batching on in Webhooks (Actions) set Batch Size to 500 or lower. That leaves room for parallel batches and your other traffic in the same 10 seconds. Segment retries a 429 but not a 413.
Known limitation: the limit is soft under parallel bursts. Each request checks the count and then stores, so requests that arrive at the same moment can all pass. In a test with 10 parallel batches, 8× the limit was stored. The limit throttles sustained traffic; it does not cap a single parallel burst.
When you get 429
The response says what happened and what to do:
{
"error": "Rate limit exceeded",
"detail": "This workspace may send at most 2000 messages per 10 seconds for events, and separately 2000 identify/group writes (counted as accounts and users touched), and every message in a batch counts. Nothing from this request was stored. Retry after 10 seconds, or send smaller batches (Segment: Batch Size 500 or lower).",
"limit": 2000,
"windowSeconds": 10
}- Nothing from the request was stored, so resending it creates no duplicates. Segment does this on its own.
- Wait for
Retry-Afterseconds before sending again. - If you hit the limit regularly, lower Batch Size first. If your normal volume needs a higher limit, contact us. The limit is set per workspace.
- Atune counts every refused message per workspace, so we can see when a workspace keeps hitting its limit.