API Integration Documentation

Connect your point-of-sale or order-management system to the AION platform to create, return, and cancel product covers in real time.

Base URL  https://api.aioncover.com Version 1.0 REST · JSON · HTTPS only

Overview

The AION API lets an authorized brand's sales system push transactional events to AION so that a protection cover (policy) is opened for each eligible item, and closed when an item is returned or a sale is cancelled.

There are three core operations, each a single POST with a JSON body:

  • Sale — register a new sale and open a cover for the purchased item(s).
  • Return — a customer returned an item; the associated cover is closed.
  • Cancellation — a sale was voided; the associated cover is closed.
How to get started. Integration is invitation-based. AION issues you a unique API token and registers your brand before you go live. To request access, contact your AION representative or team@aioncover.com.

Authentication

Every request to a /v1/external/* endpoint must include your API token in the x-api-key header, using the Bearer scheme:

x-api-key: Bearer YOUR_API_TOKEN

Your token identifies your brand and the integration it belongs to. AION resolves the brand and event source from the token server-side — you never send a brand identifier yourself.

SituationResult
Header missing, malformed, or not Bearer <token>401 Unauthorized
Token unknown, revoked, or inactive401 Unauthorized
Valid, active tokenRequest is processed
Keep your token secret. Treat it like a password. Send it only from your server over HTTPS — never embed it in a browser, mobile app, or public repository. If a token is exposed, contact AION to have it rotated. AION will never ask you to share it by email or chat.

Conventions

Requests

  • All endpoints accept Content-Type: application/json.
  • Every payload is wrapped in a single top-level envelope key (sale, return, or cancellation).
  • Fields set to null are ignored, and surrounding whitespace on string values is trimmed.
  • Unknown extra fields are accepted and ignored — sending more than is documented is safe.
  • Field names are snake_case. For backward compatibility, sub_order_row_code and sell_date are also accepted in their legacy camelCase form (subOrderRowCode, sellDate); new integrations should use snake_case.
  • Maximum request body size is 512 KB.

Responses

All responses are JSON and always include a human-readable message field. The HTTP status code is the source of truth for success or failure. Error responses have the shape:

{ "message": "..." }

Dates

Timestamps are ISO 8601 strings, e.g. 2025-08-26T10:15:00.000.

  • A value without a timezone suffix is interpreted as Europe/Rome local time and stored as UTC.
  • A value with a Z or numeric offset (e.g. +02:00) is used as-is.
  • Date/time fields are optional; when omitted, AION uses the time the request is received.

Idempotency

Covers are keyed on the combination of sale.id and row_id within your brand. Re-sending an event that was already processed is safe: AION returns 201 with a message stating the record already existed and performs no duplicate action. This makes retries safe after a network error.

Create Sale

POST /v1/external/sale Requires API token

Registers a sale and opens a cover for the item. Send one request per sold line item.

Body

Top-level envelope: { "sale": { ... } }.

FieldTypeReq.Description
idstringrequiredYour unique sale identifier.
row_idstringrequiredYour unique line-item identifier within the sale.
quantityintegerrequiredUnits sold on this line. A whole quantity ≥ 2 opens one cover per unit (see note below).
customerobjectrequiredBuyer details — see below.
sub_order_row_codestringoptionalYour sub-order / row reference code.
sell_datestring (ISO)requiredWhen the sale occurred.
itemobjectrequiredThe purchased product — see below.
shopobjectrequiredThe selling location — see below.

customer object (required)

FieldTypeReq.Description
emailstringrequiredBuyer email (lower-cased on storage). Primary match key.
first_namestringrequiredPrivate customer's first name.
last_namestringrequiredPrivate customer's last name.

Business customers may additionally send vat, entity_name, and business_address. When vat is present the record is treated as a business.

item object (required)

FieldTypeReq.Description
idstringrequiredYour product/catalogue identifier.
namestringrequiredProduct name.
categorystringrequiredProduct category.
recommended_retail_pricenumberrequiredFull retail price (RRP).
selling_pricenumberrequiredActual price paid.
skustringrequiredStock-keeping unit.
descriptionstringrequiredProduct description.
picturestring (URL)requiredImage URL.
collectionstringrequiredCollection name.
compositionstringrequiredMaterial composition.

shop object (required)

FieldTypeReq.Description
idinteger or stringrequiredYour store identifier.
namestringrequiredStore display name.
Multi-unit sales. When quantity is a whole number ≥ 2, AION opens a separate cover for each unit and returns one result entry per cover. A quantity of 1 (or a fractional quantity) produces a single cover.

Example request

curl -X POST "https://api.aioncover.com/v1/external/sale" \
  -H "Content-Type: application/json" \
  -H "x-api-key: Bearer YOUR_API_TOKEN" \
  -d '{
    "sale": {
      "id": "SALE-0001",
      "row_id": "ROW-0001",
      "sub_order_row_code": "SUB-0001",
      "quantity": 1,
      "sell_date": "2025-01-15T10:30:00.000",
      "customer": {
        "email": "customer@example.com",
        "first_name": "Jane",
        "last_name": "Doe"
      },
      "item": {
        "id": "ITEM-0001",
        "sku": "SKU-0001",
        "name": "Sample Product",
        "description": "Sample product description.",
        "picture": "https://example.com/sample.jpg",
        "category": "SAMPLE CATEGORY",
        "collection": "Sample Collection",
        "composition": "Sample material",
        "recommended_retail_price": 1000.00,
        "selling_price": 900.00
      },
      "shop": { "id": 1, "name": "Sample Store" }
    }
  }'

Example response — 200 OK

{
  "message": "Sale processed successfully.",
  "results": [
    {
      "status": 200,
      "policyId": "…",
      "message": "Cover created successfully.",
      "policyStatus": "live"
    }
  ]
}

The results array contains one entry per cover. Possible per-cover policyStatus values: live and blocked (created), existing (already present), or errored. Whether a new cover is live or blocked is decided by AION's underwriting rules.

Status codes

200Sale processed; one or more covers created.
201Processed successfully, but all covers already existed (no change made).
400Missing sale envelope or unprocessable body.
500One or more covers failed to process (see results).

Return

POST /v1/external/return Requires API token

Records that an item was returned and closes the matching cover.

Body

Top-level envelope: { "return": { ... } }.

FieldTypeReq.Description
idstringrequiredYour unique return identifier.
sale_idstringrequiredThe original sale.id.
row_idstringrequiredThe original line-item row_id.
returned_atstring (ISO)requiredWhen the return occurred.
shopobjectrequiredStore where the return was made (id, name).

Example request

curl -X POST "https://api.aioncover.com/v1/external/return" \
  -H "Content-Type: application/json" \
  -H "x-api-key: Bearer YOUR_API_TOKEN" \
  -d '{
    "return": {
      "id": "RETURN-0001",
      "sale_id": "SALE-0001",
      "row_id": "ROW-0001",
      "returned_at": "2025-01-20T14:00:00.000",
      "shop": { "id": 1, "name": "Sample Store" }
    }
  }'

Example response — 200 OK

{
  "status": 200,
  "policyId": "…",
  "message": "Cover cancelled successfully.",
  "policyStatus": "cancelled"
}

Status codes

200Cover found and closed.
201The cover was already returned or cancelled (no change made).
400No cover matches the given sale_id + row_id, or the body is unprocessable.
500Processing error.

Cancellation

POST /v1/external/cancellation Requires API token

Records that a sale was voided and closes the matching cover. Unlike a return, no shop is required.

Body

Top-level envelope: { "cancellation": { ... } }.

FieldTypeReq.Description
sale_idstringrequiredThe original sale.id.
row_idstringrequiredThe original line-item row_id.
idstringrequiredYour cancellation identifier.
cancelled_atstring (ISO)requiredWhen the cancellation occurred.

Example request

curl -X POST "https://api.aioncover.com/v1/external/cancellation" \
  -H "Content-Type: application/json" \
  -H "x-api-key: Bearer YOUR_API_TOKEN" \
  -d '{
    "cancellation": {
      "id": "CANCELLATION-0001",
      "sale_id": "SALE-0001",
      "row_id": "ROW-0001",
      "cancelled_at": "2025-01-20T14:00:00.000"
    }
  }'

Example response — 200 OK

{
  "status": 200,
  "policyId": "…",
  "message": "Cover cancelled successfully.",
  "policyStatus": "cancelled"
}

Status codes

200Cover found and closed.
201The cover was already cancelled (no change made).
400No cover matches the given sale_id + row_id, or the body is unprocessable.
500Processing error.

Health Check

GET /health No auth

Lightweight liveness probe. Returns 200 when the service is up.

curl "https://api.aioncover.com/health"

# → { "status": "ok" }

Error Reference

Errors always return a JSON body of the form { "message": "..." }. Handle responses by HTTP status:

StatusMeaningTypical message
400Bad request — malformed JSON or missing required envelope/fields.Error in processing the request body payload.
401Missing or invalid API token.Unauthorized
413Request body exceeds 512 KB.Request body too large.
429Rate limit exceeded.Too many requests.
500Unexpected server error.Internal Server Error
Retries. 429 and 5xx responses are safe to retry with exponential backoff. Thanks to idempotency (see Conventions), retrying a request that actually succeeded will not create a duplicate cover.

Limits & Security

Rate limits

  • 120 requests per minute per API token on the write endpoints. Exceeding this returns 429.
  • Request body limited to 512 KB.

Transport & platform security

  • HTTPS only. The API is served exclusively over TLS with HSTS enabled; plain HTTP is not supported.
  • Responses set hardening headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Cache-Control: no-store).
  • Tokens are never written to logs, and payloads are validated and size-capped before processing.
  • Only GET, POST, and OPTIONS methods are accepted.

Your responsibilities

  • Store your token in a server-side secret manager; never expose it client-side.
  • Call the API only from your backend.
  • Report a suspected token compromise to AION immediately for rotation.