Customer Usage Events

Pilot — Customer usage events are part of the customer risk pilot. Start there for what the feature does and how to join.

If you sell usage (API calls, tokens, minutes), send Inflow one event each time one of your customers consumes something. Inflow reviews the activity of your customers and tells you, with the risk.customer_flagged webhook, when a customer looks risky and what to do about it.

Usage events are for risk detection only. They are not invoiced and do not change any amount. The API only receives events: there is no route to read them back.

Authenticate with a secret API key (X-Inflow-Api-Key). Public and SDK keys are not accepted.

How it works

  1. Your server sends usage events, one by one or in batches of up to 100.
  2. Inflow checks each event at once and answers accepted, duplicate or an error. An accepted event is final: nothing about it is reported later.
  3. Inflow analyses the activity of your customers twice a day. A person at Inflow reviews every finding before you hear about it.
  4. When a finding is confirmed, you receive risk.customer_flagged with a level and a recommended action. You decide what to do.

You never receive a score or the rule that fired.

Send one event

POST https://api.inflowpay.com/api/customer-usage-events
X-Inflow-Api-Key: inflow_prod_xxx
Content-Type: application/json

{
  "identifier": "req_01HZX3",
  "customer_id": "cus_abc123",
  "occurred_at": 1792504992,
  "resource": "llm_tokens",
  "quantity": 1550,
  "client_ip": "203.0.113.7",
  "client_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
  "client_country": "FR",
  "api_key_id": "key_42"
}

Answer (200):

{ "status": "accepted", "id": "cue_…", "identifier": "req_01HZX3" }

Fields

FieldRequiredRules
identifierYesYour own id for the event. 1 to 255 characters among A-Z a-z 0-9 _ - : .. Unique per account, for good.
customer_idYesThe Inflow customer who consumed (cus_…). It must be one of your customers.
occurred_atYesWhen the consumption happened, as a Unix timestamp in seconds. At most 35 days in the past, 5 minutes in the future.
resourceYesWhat was consumed, in your words (a model, an endpoint). 1 to 64 characters among a-z 0-9 _ . -.
quantityYesHow much was consumed, in the unit of the resource (tokens, calls, minutes). An integer from 0 to 9007199254740991.
client_ipNoThe IPv4 or IPv6 address of your end user, as you saw it. No leading zeros in an IPv4 octet, no zone id (%eth0).
client_user_agentNoThe User-Agent header of your end user. Characters other than printable ASCII are removed; the first 512 are kept.
client_countryNoThe country of your end user, as an ISO 3166-1 alpha-2 code (FR). Inflow checks two letters and stores them in upper case.
api_key_idNoThe id of the key you gave your end user, never the key itself. 1 to 128 visible ASCII characters.

Any other field is refused. null on an optional field means "not sent".

Compare quantities within one resource only: Inflow never adds tokens to minutes.

Send a batch

Send up to 100 events in one call. A batch body can be up to 512 kB; a single event, up to 100 kB. A larger body gets a 413 and nothing is written.

POST https://api.inflowpay.com/api/customer-usage-events/batch
X-Inflow-Api-Key: inflow_prod_xxx
Content-Type: application/json

{
  "events": [
    { "identifier": "req_1", "customer_id": "cus_abc123", "occurred_at": 1792504992, "resource": "llm_tokens", "quantity": 1550 },
    { "identifier": "req_2", "customer_id": "cus_unknown", "occurred_at": 1792504993, "resource": "llm_tokens", "quantity": 20 }
  ]
}

Answer (200), one result per event, in the order sent:

{
  "results": [
    { "index": 0, "status": "accepted", "id": "cue_…", "identifier": "req_1" },
    {
      "index": 1,
      "status": "rejected",
      "error": { "code": "customer_not_found", "field": "customer_id", "message": "no customer with this id for this merchant" }
    }
  ]
}

A rejected event does not fail the others. Two valid events with the same identifier in one batch: the first is accepted, the second is duplicate. An invalid event is rejected, whatever its identifier.

A body that is not { "events": [ … ] } with 1 to 100 events, or that has another field, gets a 400 and nothing is written.

Retries and duplicates

The identifier makes every call safe to retry. An identifier already accepted answers duplicate, with the id of the first event, and nothing is written again. This is true even when the event would now be refused (for example, it is older than 35 days).

Inflow does not compare the content: an identifier already accepted answers duplicate even when the other fields differ, and the new values are not kept. Use a new identifier for each new consumption.

A rejected event reserves nothing: fix it and send it again with the same identifier.

On a timeout, a 429 or a 5xx, send the same events again. The 429 of the per-account limit carries a Retry-After header, in seconds.

Errors

A rejected single event gets a 400 with an error body. code names the error, and field names the field:

{
  "statusCode": 400,
  "message": "must be an IPv4 address (four octets) or an IPv6 address",
  "code": "invalid_client_ip",
  "field": "client_ip"
}

In a batch, the same code, field and message are in results[].error. A body that is not valid JSON gets a 400 without a code. While risk analysis is not available, both endpoints answer 503: send the events again later.

codeMeaning
invalid_requestA field is missing, has the wrong type or format, or is unknown.
invalid_client_ipclient_ip is not an IPv4 or IPv6 address.
customer_not_foundNo customer with this id on your account (another account's customer too).
occurred_at_out_of_rangeoccurred_at is more than 35 days in the past or 5 minutes in the future.

Marketplaces

A marketplace sends the events of a sub-merchant's customers with the X-On-Behalf-Of header, like any other merchant call. See Acting on behalf of. Without the header, the customer must belong to the marketplace itself. Identifiers are unique per sub-merchant.

Rate limit

Usage events have their own limit: 50 requests per second per account. They do not use the budget of your other API calls, and your other calls do not slow them down. A marketplace shares this limit with the calls it makes on behalf of its sub-merchants. To send more, use batches. See Rate limits.

Data retention

client_ip and client_user_agent are deleted 12 months after Inflow receives the event. The rest of the event is kept as the record of the usage.

Receive the recommendations

Subscribe your webhook endpoint to risk.customer_flagged. A marketplace receives risk.customer_flagged for its own customers and connect.risk.customer_flagged for the customers of its sub-merchants: subscribe to both. See Customer risk for the payload.

Inflow analyses the last 24 hours and the last 7 days of usage. An event sent late still counts, but only while it falls in one of these windows: send events as they happen.


Did this page help you?