Skip to main content

Reporting usage (activity, screentime, transactions)

This guide covers how your product sends usage data to k-ID, so that a trusted adult can see it in Family Connect. There are three entry points into the same ingestion model:

All three are report-only and need no parent setup: your product reports what happened, k-ID aggregates it, and the trusted-adult surfaces read the aggregate. None of them drives any real-time enforcement. For the full screentime feature, the parent-configured limits, quiet hours, break reminders, the session state machine, and the events it fires, see the Screentime guide. For transaction visibility, purchase approval, and payment-as-verification, see the Transactions guide.

For what these are and where their boundaries sit, see Activity and Transactions.

Before you start

All three endpoints attribute what you send to a k-ID session, so your product needs the sessionId it received from the age gate. See Sessions. Calls are server-to-server and authenticated with an API key, as described in Authentication.

Beyond the session, each path has one prerequisite:

  • Activity needs a declared activity type. Every record names a type your product has declared in Compliance Studio, and a type is local until you push it to the environment your API key serves. See Activity types and Testing and publishing.
  • Screentime needs the Screentime feature enabled for the product. Without it, /screentime/push returns FEATURE_DISABLED.
  • Transactions needs the Transactions feature enabled for the product. Without it, /transaction/push returns FEATURE_DISABLED.

Three ingestion endpoints

The three endpoints share one model and differ only in what a single item carries.

/activity/push/screentime/push/transaction/push
Acceptsrecords against declared activity typescompleted screentime usage segmentscompleted purchases
Batch field, maxrecords, up to 1000events, up to 100events, up to 100
Each item carriesid, type, value, timestamp, optional attributesid, durationSeconds, timestampid, title, amount, currency, timestamp, status, optional description and url
Prerequisitea declared, pushed activity typethe Screentime feature enabledthe Transactions feature enabled
Real-time eventsnonenone (the live path is in the Screentime guide)none (the live path is in the Transactions guide)

What they have in common is the whole ingestion model: each item carries a client-generated id for idempotency and a timestamp within the last seven days, a batch covers exactly one session, items are validated one at a time with a per-item accept or reject, and what lands feeds the aggregate a trusted adult sees rather than triggering anything.

Pushing activity records

Send a batch of records for one session to /activity/push.

type is the activity type's key: the identifier k-ID generates when you create the type, a UUID. Copy it from the activity type's configuration in Compliance Studio rather than constructing it.

Replace timestamp with the moment the activity happened. The example value is illustrative: a timestamp more than seven days old is rejected, so a copied example goes stale.

Example request

POST /api/v1/activity/push
Content-Type: application/json
Authorization: Bearer your-api-key

{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"records": [
{
"id": "7c3e0c6e-9c2a-4f1e-bd2b-1f9a3c6d8e10",
"type": "2f9a1c84-6b3e-4d52-9f08-1a7c3e5d9b20",
"value": { "seconds": 1800 },
"timestamp": "2026-08-19T14:32:05Z",
"attributes": { "mode": "co_op" }
}
]
}

Example response

{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"accepted": 1,
"rejected": 1,
"records": [
{ "id": "7c3e0c6e-9c2a-4f1e-bd2b-1f9a3c6d8e10", "status": "accepted" },
{ "id": "a4f2b8d1-3e7c-4a90-8b6f-2c5d9e0a1b34", "status": "rejected", "reason": "timestamp_too_old" }
]
}

A 200 response doesn't mean every record landed: check rejected and the per-record status rather than the HTTP status alone.

Value shapes

Each activity type declares a metric type, and a record's value object must carry that metric type's fields and nothing else. A record that mixes fields, such as seconds alongside count, is rejected even when both are valid on their own.

Metric typevalueNotes
duration{ "seconds": 1800 }Whole seconds, zero or greater
currency{ "amount": 999, "currency": "USD" }amount in minor units, zero or greater. currency is an ISO 4217 code
count{ "count": 7 }Zero or greater
gauge{ "number": 0.85 }Any double
boolean{ "bool": true }

Currency codes are matched against ISO 4217, so an in-game currency name such as GEMS is rejected. Report virtual-currency purchases as a count, or as a real-money amount if that's what you want a parent to see.

Attributes

attributes is optional key/value metadata for dimensions you want broken out, such as which mode of your product the time was spent in. Each type declares the attribute keys it accepts, and the rules are strict:

  • A record can carry at most 20 attributes.
  • Keys are at most 64 characters, values at most 256.
  • A key that the type doesn't declare is rejected. A type that declares no attribute keys accepts no attributes.
  • Values that look like an email address, a phone number, or a payment card number are rejected.

Send an opaque identifier your own systems can resolve rather than personal data.

Activity rejection reasons

ReasonWhat happened
invalid_idid isn't a UUID
unknown_typetype isn't declared for this product in the configuration the API key's mode serves
invalid_valuevalue doesn't match the type's metric type, is negative, or carries fields from more than one metric type
timestamp_in_futuretimestamp is later than now
timestamp_too_oldtimestamp is more than seven days old
attributes_too_manyMore than 20 attributes
attribute_key_too_longAn attribute key is longer than 64 characters
attribute_value_too_longAn attribute value is longer than 256 characters
attribute_key_not_allowedAn attribute key isn't declared for this type
attribute_value_forbidden_contentAn attribute value looks like an email address, a phone number, or a payment card number
type_metric_type_unsupportedThe type's metric type isn't one this API version handles
transient_errorThe record was valid but couldn't be stored. Retry it

Every reason except transient_error describes a record your product has to change. Retrying an unchanged record produces the same rejection, so route these to your logs rather than to a retry queue.

Pushing screentime usage

/screentime/push reports completed segments of usage after the fact, for a server that buffers segments or a game engine that reports totals on shutdown. Each accepted segment adds its durationSeconds to the child's daily screentime total and is displayed in the parent's Family Connect charts, the same shape of ingestion as activity records.

It's report-only. It fires no break reminder, no limit warning, and no limit-reached event, because those are scheduled at the moment a session starts, not reconstructed after the fact. If your product needs those real-time signals, or the parent-configured limits, quiet hours, and overrides behind them, that's the live path in the Screentime guide, not this endpoint.

Example request

POST /api/v1/screentime/push
Content-Type: application/json
Authorization: Bearer your-api-key

{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"events": [
{
"id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e",
"durationSeconds": 1800,
"timestamp": "2026-06-24T09:00:00Z"
}
]
}

Example response

{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"accepted": 1,
"rejected": 1,
"events": [
{ "id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e", "status": "accepted" },
{ "id": "c1b2a3d4-5e6f-4708-9a1b-2c3d4e5f6071", "status": "rejected", "reason": "invalid_input" }
]
}

As with activity, check rejected and the per-event status rather than the HTTP status alone.

Segment rules

Each event is one completed segment, carrying its own id for idempotency, the timestamp (UTC) when the span of usage began, and its durationSeconds.

  • durationSeconds is between 1 second and 24 hours. Split a longer span into several segments.
  • The segment must already have completed: timestamp + durationSeconds can't be in the future.
  • timestamp must be within the last seven days.
  • A segment that crosses midnight in the parent's configured time zone is split across both dates, so each day's total reflects the usage that happened on that date.
  • Up to 100 segments per call.

Screentime rejection reasons

ReasonWhat happened
invalid_inputThe segment broke a validation rule: a bad id, a durationSeconds outside 1 second to 24 hours, or a timestamp out of range. Retrying it unchanged fails again
internal_errorA transient storage failure. Safe to retry

Pushing transactions

Send a batch of completed purchases for one session to /transaction/push. It's report-only, the same as the other two pushes: each purchase you report is recorded and surfaced to the linked parent in Family Connect, and reporting one never blocks a charge.

The Transactions feature must be enabled for the product; without it, /transaction/push returns FEATURE_DISABLED. Each accepted purchase also feeds activity as a currency-metric event, so reported purchases flow through the same aggregate a trusted adult reads.

Each item carries the purchase: a title, an amount in the currency's minor units, a currency, the timestamp it completed, and a status of successful or failed. For the full field set, the request and response shapes, and the per-event statuses, see Reporting purchases in the transactions guide.

Batch-level errors

Some conditions fail the whole call, and the response carries an error code instead of per-item results. See Error handling.

ErrorCause
NOT_FOUNDNo session with that sessionId for this product
INVALID_INPUTThe session is revoked, the batch is empty or over its size limit, or the body doesn't parse
FEATURE_DISABLED/screentime/push was called without the Screentime feature enabled, or /transaction/push without the Transactions feature

Retries and idempotency

All three endpoints take a client-generated id on every item, a record's id on /activity/push, a segment's id on /screentime/push, and a purchase's id on /transaction/push, and that id is what makes a retry safe. Pushing an item whose id was already accepted counts as accepted again and stores nothing new, so a batch that timed out mid-flight can be sent again as-is.

That only holds if the id is stable. Generate it once, when the usage happens, and store it with the item you're about to send. An id generated at send time turns every retry into a duplicate.

Choosing a batching strategy

The seven-day timestamp window is the outer bound, not a target. Two things push toward smaller, more frequent batches:

  • A trusted adult sees usage only after it arrives, so a product that pushes daily shows a parent yesterday's picture.
  • Items older than seven days are rejected outright. For activity, backfills beyond that window must use the async file-upload path; screentime segments have no such path, so report them within the window.

Batch per session for a few minutes of play, within each endpoint's size limit (1000 records, 100 segments, or 100 purchases), and both problems stay away.

Verifying the integration

/activity/send-test-digest renders the digest email a linked adult receives and sends it, using the session's real data from the trailing seven days. The digest reflects the screentime, activity, and purchases a session accrued, so it's the fastest way to confirm that what you're pushing turns into something a parent can read.

Example request

POST /api/v1/activity/send-test-digest
Content-Type: application/json
Authorization: Bearer your-api-key

{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672"
}

Example response

{
"sent": true
}

Four things to know about it:

  • Test mode only. A live API key gets a NOT_FOUND error. The endpoint can never email a real linked adult.
  • The session needs a linked adult. A session whose hasApproverEmail is false returns INVALID_INPUT.
  • sent: false is a success. It means the trailing week had nothing to summarize, so no email went out.
  • It covers the trailing seven days. The window ends today, so data you pushed for this session is included.

Pass an optional locale, such as ja, to preview a translation of the email.

Integration checklist

  • Activity types declared in Compliance Studio and pushed to the environment your API key serves, the Screentime feature enabled if you push screentime, and the Transactions feature enabled if you push transactions.
  • Item ids generated where the usage happens, and stored alongside the item so retries reuse them.
  • Response handling that reads rejected and the per-item reason, alerts on a rejection-rate change, and retries only the transient reason (transient_error for activity, internal_error for screentime and transactions).
  • Timestamps in UTC, and a push cadence well inside the seven-day window.
  • Attributes limited to declared keys, carrying no personal data.
  • A test digest sent and read end to end.