Skip to main content
A request that times out has an unknown outcome: it may have created a record, or it may not. Sending it again without a key is how a single detention charge becomes two. The Alvys Public API gives you two separate tools, for two separate problems. They are not interchangeable. An idempotency key protects a record that may not exist yet; an ETag protects one that already does.

Retrying a create with ExternalId

Send your own unique id with the create. If the same id arrives again, Alvys returns the record the first call created instead of writing a second one.
Retry the identical request and you get the original charge back. You do not get a second one, and you do not need to check first.

The key is the request, not just the id

Reusing a key with different money is refused, not applied: A correction is a new record, not a retry — give it a new key. An update to an existing record is a different operation entirely; see below.
Scope the key to the thing it identifies. detention-2026-09-09 is too coarse if a trip can carry two detention charges on one day — the second is refused as a conflict. Include the stop, the event, or whatever makes this charge distinct.

Choosing a key

  • Stable across retries. A key regenerated on each attempt offers no protection at all. Derive it from something in your own system — an event id, a row id — not from a timestamp or a random value created at send time.
  • Unique within your tenant. Two tenants may use the same string safely; two charges in one tenant may not.
  • Case-sensitive, and compared after surrounding whitespace is trimmed. ABC and abc are two different keys.
  • Optional. Omit it and no idempotency is offered — a retry creates a second record.

503 means retry, not fail

A 503 Service Unavailable on a keyed create means the write could not be confirmed and may already exist. Retry the identical request. Recording it as uncreated is the one response that produces a lost charge, because the record may be there.

Updating an existing record

An update carries the opposite risk: the record exists, and someone else may have changed it since you read it. That is a precondition problem, not an idempotency one, and it is handled with an ETag and an If-Match header rather than a key. See Updating Records Safely.

Using both together

A create and an update use different mechanisms, and a robust integration uses both:
  1. Create with an ExternalId you derived from your own record. A retry is safe.
  2. Keep the ETag from the create response.
  3. Update with If-Match. A stale validator tells you to re-read rather than silently overwriting.
Reusing the create’s ExternalId on an update does nothing — the key belongs to the create, and an update is addressed by the record id in the path.