Sports Inventory Reliability and Troubleshooting

Sports Inventory ingestion is asynchronous. Build the client around acceptance, safe retries, deterministic entity keys, and useful diagnostics.

What 202 Accepted means

The API validates the request and accepts the whole JSON array for asynchronous processing. A successful default response is:

HTTP/1.1 202 Accepted
Content-Type: application/json
{
  "success": true
}

This confirms acceptance only. It does not confirm that:

  • every entity is already visible to downstream Optimove Personalize experiences;
  • all hierarchy references point to existing parents;
  • a PUT matched an existing row;
  • a DELETE matched an existing row;
  • downstream processing has completed.

There is currently no public endpoint for polling processing status.

Inspect the body of every 202

An acceptance failure can be represented as:

{
  "success": false
}

Treat 202 plus success: false as retryable. Do not rely on the HTTP status alone.

Get an acceptance reference

Send this fixed protocol media type:

Accept: application/vnd.optimove.optix.inventory.records.v2+json

The response adds an acceptance reference for each request:

{
  "success": true,
  "records": [
    "rcpt-3f9a2c7e14b8"
  ]
}

The full request array is accepted as a single unit, so the reference represents the whole HTTP batch rather than an individual Category, Event, or Selection. Treat it as an opaque string: retain it for support diagnostics, but do not parse or depend on its format, which may change.

Use a returned reference as an acceptance confirmation only when success is true. If acceptance fails, the response can contain "unknown" instead of a reference. Keep the complete response and retry the whole batch.

Persist these values with:

  • UTC request timestamp;
  • HTTP method;
  • brand key, masked where required by your security policy;
  • entity count and entity types;
  • a client-generated correlation ID in your own logs;
  • HTTP status and parsed response body.

Do not put an unsupported correlation field into the Sports Inventory JSON. Keep it in your own request log.

Retry policy

Use bounded exponential backoff with jitter. Honor Retry-After when it is present.

OutcomeRetry?Action
202 with success: trueNoRecord acceptance and continue
202 with success: falseYesRetry the same batch
400 validation errorNoCorrect the request body, headers, or field values
401 or 403No, unless credentials were refreshedCheck the API key, brand key, and Sports Inventory entitlement
429YesHonor Retry-After; back off with jitter
5xxYesRetry with bounded exponential backoff and jitter
Connection failure or timeout before a responseYes, cautiouslyOutcome is ambiguous; retry the identical batch and consider overwrite semantics

Do not retry forever. After the client’s retry budget is exhausted, preserve the payload and diagnostics for controlled replay or support investigation.

Why ambiguous retries need care

There is no public idempotency-key header.

  • POST is a full-row upsert. A retry can replace a row and clear optional fields omitted from the retried payload.
  • PUT is a partial top-level update. Repeating the same update normally converges, but a late retry can overwrite a newer value.
  • DELETE of an already absent key is normally a no-op.
  • Across concurrent requests, global ordering is not guaranteed.

Use deterministic keys, send complete POST records, serialize dependent changes, and avoid concurrent writes to the same entity.

Validation error bodies

Validation errors can appear in either of these shapes, depending on where validation failed:

{
  "error": {
    "message": "Bad Request",
    "errors": "Validation details"
  }
}
{
  "message": "Bad Request",
  "errors": "Validation details"
}

Your error parser should tolerate both while treating the contents as diagnostic text, not a stable machine-readable error-code taxonomy.

Troubleshooting table

SymptomLikely causeWhat to check
400 Bad RequestInvalid JSON, missing required field, wrong enum case, invalid date-time, invalid Selection pricing state, POST/PUT batch outside 1–500, or X-Expires-After below 60Validate against the Sports Inventory API Reference (field types and required fields) and review Hierarchy: concepts and conventions for pricing states and enum casing; log the returned validation details
401/403Missing, empty, invalid, or unauthorized credentialsCheck x-api-key, x-brand-key, environment, and service entitlement; never print the full API key
429Request throttlingHonor Retry-After, reduce concurrency, and apply backoff with jitter
202 with success: falseThe batch was not acceptedRetry the identical batch; retain the response
Timeout or connection resetNo reliable response reached the clientRetry cautiously; use deterministic keys and account for POST replacement semantics
202 with success: true, but entity is not yet visibleProcessing is asynchronous, a parent is missing, a status hides the entity, a PUT targeted a missing row, or downstream processing failedWait for normal asynchronous processing, verify the hierarchy and state fields, then collect diagnostics; there is no public status endpoint
Selection is absent from common recommendation resultsSelection, Market, or Event is inactive; the Selection or a parent is NotDisplayed; price is unusable; parent join is brokenCheck exact enum case, every level’s display state, parent keys, and odds fields
Decimal odds did not change as expectedPUT changed only fractional fields or supplied inconsistent pricesSend numPrice, denPrice, and decPrice together
Child records remain after a parent deleteMultiple same-type parent deletes were batchedSend each parent in a separate DELETE request
Translations or extra keys disappearedPUT replaced the whole nested objectSend the complete desired object, not only the nested key being changed
Event lifecycle timing is unexpectedMissing or invalid eventEndDateTime, timezone mismatch, or expiry/housekeeping is still processingUse UTC for the end time and remember that an omitted or earlier end becomes start plus 24 hours

Hierarchy diagnostics

When a Selection is not usable, verify the full chain:

Selection.marketKey
  = Market.marketKey

Market.eventKey
  = Event.eventKey

Event.typeKey
  = Type.typeKey

Type.classKey
  = Class.classKey

Class.categoryKey
  = Category.categoryKey

Then verify:

  • selectionStatus, marketStatus, and eventStatus are Active where the entity should be available;
  • displayStatus is Displayed on the Selection and every Category, Class, Type, Event, and Market in its path where that field is supplied;
  • eventDateTime is correct and timezone-aware;
  • fractional odds have both numPrice and denPrice, with a denominator greater than zero, or a usable decPrice is present;
  • eventKey on the Selection agrees with its Market’s Event;
  • keys match exactly, including case and punctuation.

Batch design

  • Maximum documented POST/PUT record count is 500 per request.
  • The record-count limit does not establish a separate public payload-byte limit.
  • Large extra and translation objects make a batch larger; split earlier when needed.
  • Avoid mixing unrelated hierarchy branches in a very large batch while validating a new integration.
  • Avoid duplicate entity keys in the same batch; the first duplicate wins.
  • Use parent-to-child order for POST.
  • Send parent deletes individually when relying on cascade behavior.

Expiry behavior

X-Expires-After is an ingestion-time TTL, not an exact scheduling API.

  • Minimum value is 60 seconds.
  • One value applies to every record in the request.
  • It is available on POST and PUT, not DELETE.
  • POST without the header clears an old expiry as part of its full-row replacement; PUT without the header preserves an existing expiry.
  • Removing an existing expiry requires a complete authoritative POST without the header.
  • Records are removed asynchronously after they become eligible.
  • A request that reaches processing after its TTL has already elapsed can be filtered out.

Use Event status and explicit deletes for deterministic business-state changes. Use TTL for temporary inventory where asynchronous expiry is acceptable.

Information to provide to support

If a request was accepted but the expected state is not available, provide:

  1. environment and base URL placeholder value used, without credentials;
  2. UTC request timestamp and HTTP method;
  3. brand key or an approved masked form;
  4. entity types and count;
  5. sanitized request body containing the relevant keys and hierarchy;
  6. HTTP status and full response body;
  7. vendor response record identifier, if requested;
  8. client retry history, including timestamps;
  9. expected state and observed state.

Never send the full x-api-key in logs, tickets, or screenshots.

Production-readiness checklist

  • Credentials are stored in a secrets manager and never logged.
  • Every response body is parsed, including 202 responses.
  • 429, 5xx, and ambiguous transport failures use bounded backoff with jitter.
  • Validation and authentication failures are not blindly retried.
  • POST payloads are complete authoritative rows.
  • PUT nested objects include the complete desired object.
  • Odds PUTs send consistent fractional and decimal values together.
  • POST batches are parent-first and contain no duplicate keys.
  • Parent deletes are sent individually.
  • Vendor acceptance identifiers and request metadata are retained for diagnostics.
  • The integration does not depend on a public processing-status endpoint.

Did this page help you?