Agent web access

Replenit’s production agents may access customer websites to retrieve and process publicly available content required for enrichment, tone adaptation, product understanding, and related agent-based workflows.

To prevent these requests from being blocked by rate limiting, bot protection, web application firewalls, or other security controls, allowlist the following static egress IP addresses:

text
34.12.230.237
34.91.210.3

All outbound requests from Replenit’s production agent infrastructure originate from these IP addresses.

i
Please confirm once the IP addresses have been allowlisted. Upon request, Replenit can also provide additional information, including:
  • Expected request volume and traffic patterns
  • User-agent details
  • Target domains and URL paths
  • Request frequency and concurrency
  • Environment-specific access requirements
Integration API

API Reference

Send customer profiles, order history, and product catalogue data into Replenit. Once connected, the decision engine begins making 1:1 replenishment, cross-sell, and retention decisions automatically.

i
This reference covers all data ingestion and deletion endpoints. For the platform user guide, see the Platform Guide.

Overview

Replenit exposes three POST endpoints for ingesting data (customers, orders, and products) and four DELETE endpoints for removing records. All POST endpoints are upsert operations: resending the same ID updates the existing record.

Customers
Identity, contact details, and channel availability flags used for behavioural profiling and decision targeting.
Orders
Purchase history with full line-item detail. The primary input for customer enrichment and replenishment timing models.
Products
Catalogue with variants, pricing, and inventory. Enriched automatically with semantic attributes by the reasoning layer.

Authentication

Every request requires two values: your Tenant ID passed in the URL path, and your API key in the x-replenit-auth-key header.

Header
x-replenit-auth-key
Value
Your base64-encoded API key
Tenant ID
GUID, included as a path parameter on every request

Retrieve both from your dashboard under Settings → API Keys.

http
POST https://api.replen.it/customers/{tenantId}
x-replenit-auth-key: YOUR_API_KEY
Content-Type: application/json
!
Keep your API key secret. Do not expose it in client-side code or public repositories. Rotate compromised keys immediately in Settings.

Base URL & endpoints

Basehttps://api.replen.it
EndpointMethodPurposeBatch
/customers/{tenantId}POSTUpsert customer profiles100–500
/customers/{tenantId}DELETEDelete customer by ID or emailSingle record
/orders/{tenantId}POSTUpsert orders with line items50–200
/orders/{tenantId}/{orderId}DELETEDelete an entire orderSingle record
/orders/{tenantId}/{orderId}/itemsDELETEDelete a single line itemSingle record
/products/{tenantId}POSTUpsert product catalogue50–100
/products/{tenantId}/{productId}DELETEDelete product and all variantsSingle record
/products/{tenantId}/{productId}/variants/{variantId}DELETEDelete a single variantSingle record
i
DELETE endpoints are asynchronous. They return success once the request is durably accepted and queued. Deletions are applied idempotently downstream.

Data flow

Once your data is connected, it passes through five stages before decisions are dispatched. The API integration covers the first stage. The remaining stages run automatically inside Replenit.

Integration sequence

A typical initial integration follows this order. After the initial load, each endpoint syncs independently in real time.

Step 1
Send Products
Step 2
Send Customers
Step 3
Send Orders
Ongoing
Stream delta events
Initial load
Send your full product catalogue, existing customer base, and complete order history. This seeds the reasoning layer and allows enrichment to begin immediately.
Ongoing sync
Stream new orders, product updates, and customer changes as they occur. Each event updates the relevant enrichment context and may trigger a new decision cycle.
Delta events
All endpoints are upsert operations: resend only what has changed. Cancelled orders, inventory updates, and consent changes should be pushed as they happen, not batched.

Internal pipeline

Stage 1
Data Sources
You push customers, orders, and products via the API.
Stage 2
Data Ingestion & Quality
Schema validation, duplicate resolution, exclusion checks, and SKU linkage.
Stage 3
Reasoning Layer
Product and customer enrichment run in parallel. Each record is interpreted and contextualised.
Stage 4
Maestro
The 1:1 orchestrator evaluates every customer against active agents and selects the highest-priority action.
Stage 5
Decisions & Dispatching
Decisions are forwarded to connected platforms in an executable format ready for activation.

Dashboard data and health metrics reflect ingested records within 24 hours of your first sync.

Response format

All POST endpoints return the same { success, message, data } envelope. data is an ingestion receipt: a per-record breakdown of what the API accepted and what it rejected.

!
Check rejectedCount, not the HTTP status. A batch containing invalid records still returns HTTP 200 with "success": true and a "… saved." message, even when every record in it was rejected. A client that only checks the status code will lose data silently.

Receipt fields

FieldTypeMeaning
countintegerNumber of records read from the request body.
acceptedarrayOne entry per accepted record: { index, recordRef }.
acceptedCountintegerLength of accepted.
rejectedarrayOne entry per rejected record, each carrying index, recordRef, reason, and a violations array naming the fields that failed.
rejectedCountintegerLength of rejected. This is the field your integration should branch on.
warningsarrayNon-blocking rule findings, aggregated per rule across the whole batch rather than per record.
schemaVersionstringVersion identifier of the validation schema that processed the request, e.g. Product/v1.
schemaSourcestringOrigin of the validation schema.
schemaHashstring | nullIntegrity hash of the validation schema, or null.
processedAtstringUTC timestamp, ISO 8601 with seven fractional digits.

schemaVersion, schemaSource, and schemaHash are internal versioning fields Replenit uses to track which validation schema processed the request. They are informational — integrations do not need to read or act on them.

Example: all records accepted

json (200)
{
  "success": true,
  "message": "Products saved.",
  "data": {
    "count": 4,
    "accepted": [
      { "index": 0, "recordRef": "productId=P-SMK-1001" },
      { "index": 1, "recordRef": "productId=P-SMK-1002" },
      { "index": 2, "recordRef": "productId=P-SMK-1003" },
      { "index": 3, "recordRef": "productId=P-SMK-1004" }
    ],
    "acceptedCount": 4,
    "rejected": [],
    "rejectedCount": 0,
    "warnings": [],
    "schemaVersion": "Product/v1",
    "schemaSource": "GlobalTemplate",
    "schemaHash": null,
    "processedAt": "2026-09-02T16:17:03.5026951Z"
  }
}

Example: record rejected

A product whose variant is missing the required variantId. Note that the response is still HTTP 200 with "success": true — only rejectedCount reveals that nothing was stored.

json (200 · record rejected)
{
  "success": true,
  "message": "Products saved.",
  "data": {
    "count": 1,
    "accepted": [],
    "acceptedCount": 0,
    "rejected": [
      {
        "index": 0,
        "recordRef": "productId=SMK-DOC-REJ",
        "reason": "schema_enforce",
        "violations": [
          {
            "path": "productVariants[0].variantId",
            "rule": "required",
            "mode": "Enforce",
            "detail": null
          }
        ]
      }
    ],
    "rejectedCount": 1,
    "warnings": [
      {
        "path": "taxonomy",
        "rule": "required",
        "mode": "Warn",
        "occurrences": 1,
        "sampleIndexes": [0]
      }
    ],
    "schemaVersion": "Product/v1",
    "schemaSource": "GlobalTemplate",
    "schemaHash": null,
    "processedAt": "2026-09-07T13:33:41.8504594Z"
  }
}

Rejected records are not processed into the platform. They are retained for review for ten days. Fix the fields listed under violations and resend the record.

Example: accepted with warnings

Warn-level findings never reject a record. It is stored and processed normally, and the finding is reported in warnings.

json (200 · accepted with warnings)
{
  "success": true,
  "message": "Products saved.",
  "data": {
    "count": 1,
    "accepted": [{ "index": 0, "recordRef": "productId=SMK-DOC-WARN" }],
    "acceptedCount": 1,
    "rejected": [],
    "rejectedCount": 0,
    "warnings": [
      {
        "path": "productVariants[].originalPrice",
        "rule": "required",
        "mode": "Warn",
        "occurrences": 1,
        "sampleIndexes": [0]
      }
    ],
    "schemaVersion": "Product/v1",
    "schemaSource": "GlobalTemplate",
    "schemaHash": null,
    "processedAt": "2026-09-07T13:33:41.4194541Z"
  }
}

Reading the receipt

  • Warnings and violations are different shapes. warnings are aggregated per rule across the whole batch, with occurrences, sampleIndexes, and a template-form path such as productVariants[].currency. violations are per record with a concrete index, e.g. productVariants[0].variantId. The two cannot be joined on path.
  • detail can be null. Required-rule violations carry no detail text; value rules do, e.g. "length 11 is below 12". Do not assume it is always a string.
  • recordRef can be null. When the missing field is the identifier itself, there is nothing to name the record by. Fall back to index to map the entry back to your request array.

POST /customers/{tenantId}

POSThttps://api.replen.it/customers/{tenantId}
Create or update customer profiles. Accepts a JSON array. CustomerId or Email is the upsert key. At least one is required.

Request body: array of customer objects

Identity(4 fields)
i
At least one of CustomerId or Email is required. They can be sent individually or together. Send whichever identifier your dispatch destination can resolve. See the note below.
FieldTypeDescription
CustomerIdstringRequired*Your unique customer identifier. Max 100 chars. Upsert key. Sending the same value updates the existing record. Required if not sending Email.
EmailstringRequired*Valid email address. Upsert key when CustomerId is absent. Required if not sending CustomerId.
NamestringOptionalFirst name. Included in the decision payload for personalisation at the execution layer.
SurnamestringOptionalLast name / surname.
!
Match your identifier to your dispatch destination. Confirm whether your marketing automation platform or data platform resolves incoming decision events on email or customer ID. If you send only CustomerId but your destination cannot resolve it, decisions arrive but cannot be matched to a record. When in doubt, send both.
Contact(3 fields)
FieldTypeDescription
PhonestringOptionalE.164 format, e.g. +14155550123. Profiling signal; passed in the decision payload to downstream platforms.
LanguagestringOptionalBCP 47 tag, e.g. en-US, es-ES. Used by the reasoning layer to contextualise decisions and match localised product content.
UsernamestringOptionalPlatform username. Included in the decision payload for personalisation at the execution layer.
Channel availability flags(4 boolean fields)

Optional flags indicating which channels are available for this customer. Replenit uses them as behavioural profiling signals: understanding which channels a customer is reachable on informs how decisions are contextualised. Consent management and message suppression are handled by your marketing automation platform at execution time.

FieldTypeDescription
EmailOptinbooleanOptionalCustomer is reachable via email.
SmsOptinbooleanOptionalCustomer is reachable via SMS.
WhatsappOptinbooleanOptionalCustomer is reachable via WhatsApp.
AppPushOptinbooleanOptionalCustomer is reachable via mobile push notification.
Profiling & pipeline flags(2 fields)
FieldTypeDescription
GdprOptinbooleanOptionalControls whether Replenit is permitted to process and profile this customer. When false, no processing, enrichment, or decision generation occurs. Defaults to false. Only send customers for whom you have obtained valid consent. Replenit does not manage consent collection, validation, or staleness. That responsibility sits with your systems before data reaches this API.
IsExcludedbooleanOptionalWhen true, this customer is excluded from the decision pipeline entirely. Use for internal test accounts. Defaults to false.

Example request

json
[
  {
    "CustomerId": "C-123",
    "Email": "john.doe@example.com",
    "Name": "John",
    "Surname": "Doe",
    "Phone": "+14155550123",
    "Language": "en-US",
    "EmailOptin": true,
    "SmsOptin": false,
    "WhatsappOptin": true,
    "AppPushOptin": true,
    "GdprOptin": true,
    "IsExcluded": false
  }
]

Code examples

curl
curl -X POST "https://api.replen.it/customers/YOUR_TENANT_ID" \
  -H "Content-Type: application/json" \
  -H "x-replenit-auth-key: YOUR_API_KEY" \
  -d '[{"CustomerId":"C-123","Email":"john@example.com","GdprOptin":true}]'
javascript
const res = await fetch(`https://api.replen.it/customers/${TENANT_ID}`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-replenit-auth-key": API_KEY
  },
  body: JSON.stringify([
    { CustomerId: "C-123", Email: "john@example.com", GdprOptin: true }
  ])
});
const { success, data } = await res.json();
python
import requests

resp = requests.post(
    f"https://api.replen.it/customers/{TENANT_ID}",
    headers={"x-replenit-auth-key": API_KEY},
    json=[{"CustomerId": "C-123", "Email": "john@example.com", "GdprOptin": True}]
)
print(resp.json())

Response

Returns200400403404

Branch on data.rejectedCount, not the HTTP status. See Response format for the full receipt.

json (200)
{
  "success": true,
  "message": "Customers saved.",
  "data": {
    "count": 2,
    "accepted": [
      { "index": 0, "recordRef": "customerId=C-SMK-1001" },
      { "index": 1, "recordRef": "customerId=C-SMK-1002" }
    ],
    "acceptedCount": 2,
    "rejected": [],
    "rejectedCount": 0,
    "warnings": [],
    "schemaVersion": "Customer/v1",
    "schemaSource": "GlobalTemplate",
    "schemaHash": null,
    "processedAt": "2026-09-02T16:17:03.5026951Z"
  }
}

DELETE /customers/{tenantId}

DELETEhttps://api.replen.it/customers/{tenantId}
Delete a customer record by customerId or email. Pass either as a query parameter. Asynchronous, queued and applied idempotently.
Query paramTypeDescription
customerIdstringOptionalDelete by customer ID. Use this or email.
emailstringOptionalDelete by email address. Use this or customerId.
curl
curl -X DELETE "https://api.replen.it/customers/YOUR_TENANT_ID?customerId=C-123" \
  -H "x-replenit-auth-key: YOUR_API_KEY"

POST /orders/{tenantId}

POSThttps://api.replen.it/orders/{tenantId}
Ingest order events with full line-item detail. Each order object must include an OrderItems array containing at least one item.

Request body: array of order objects

Order-level fields(10 fields)
FieldTypeDescription
OrderIdstringRequiredUnique order identifier. Upsert key: resend to update (e.g. cancellations).
CustomerIdstringRequiredLinks to the customer's CustomerId.
EmailstringRequiredCustomer email. Used as a fallback if CustomerId is not matched.
OrderDatestringRequiredISO 8601 UTC datetime, e.g. 2025-08-22T21:00:00Z. Baseline for replenishment timing.
TotalRevenuenumberRequiredTotal order value as a decimal in the order's currency.
CurrencystringRequiredISO 4217 code, e.g. USD, EUR, GBP.
OrderItemsarrayRequiredArray of line item objects. Minimum one item required.
TotalQuantityintegerOptionalTotal units across all line items.
UniqueQuantityintegerOptionalNumber of distinct product SKUs in the order.
IsOrderCancelledbooleanOptionalSet to true for cancelled orders. Excluded from timing models. Defaults to false.
Order item fields(9 fields per item)
FieldTypeDescription
ProductIdstringRequiredLinks to the product's ProductId.
SkustringRequiredMust exactly match a ProductVariants[].Sku. Case-sensitive.
QuantityintegerRequiredUnits purchased. Used to calculate depletion rate.
PricenumberRequiredActual price paid per unit after discounts.
OriginalPricenumberOptionalFull retail price before discounts.
SizestringOptionalSize or volume, e.g. 500ml, XL.
ColorstringOptionalColour of the variant purchased.
BrandstringOptionalBrand name of the item.
IsGiftbooleanOptionalWhen true, item was a gift. Replenit adjusts replenishment timing accordingly. Defaults to false.

Example request

json
[
  {
    "OrderId": "O-789",
    "CustomerId": "C-123",
    "Email": "john.doe@example.com",
    "OrderDate": "2025-08-22T21:00:00Z",
    "TotalRevenue": 89.97,
    "Currency": "USD",
    "TotalQuantity": 3,
    "UniqueQuantity": 2,
    "IsOrderCancelled": false,
    "OrderItems": [
      {
        "ProductId": "P-42",
        "Sku": "SKU-42-RED-L",
        "Quantity": 2,
        "Price": 29.99,
        "OriginalPrice": 39.99,
        "Size": "L",
        "Color": "red",
        "Brand": "Acme",
        "IsGift": false
      },
      {
        "ProductId": "P-43",
        "Sku": "SKU-43-BLUE-M",
        "Quantity": 1,
        "Price": 29.99,
        "IsGift": true
      }
    ]
  }
]

Code examples

curl
curl -X POST "https://api.replen.it/orders/YOUR_TENANT_ID" \
  -H "Content-Type: application/json" \
  -H "x-replenit-auth-key: YOUR_API_KEY" \
  -d @orders.json

Response

Returns200400403

Branch on data.rejectedCount, not the HTTP status. See Response format for the full receipt.

json (200)
{
  "success": true,
  "message": "Orders saved.",
  "data": {
    "count": 2,
    "accepted": [
      { "index": 0, "recordRef": "orderId=O-SMK-1001" },
      { "index": 1, "recordRef": "orderId=O-SMK-1002" }
    ],
    "acceptedCount": 2,
    "rejected": [],
    "rejectedCount": 0,
    "warnings": [],
    "schemaVersion": "Order/v1",
    "schemaSource": "GlobalTemplate",
    "schemaHash": null,
    "processedAt": "2026-09-02T16:17:03.5026951Z"
  }
}

DELETE /orders/{tenantId}/{orderId}

DELETEhttps://api.replen.it/orders/{tenantId}/{orderId}
Delete an entire order and all its line items. Asynchronous.
curl
curl -X DELETE "https://api.replen.it/orders/YOUR_TENANT_ID/O-789" \
  -H "x-replenit-auth-key: YOUR_API_KEY"

DELETE /orders/{tenantId}/{orderId}/items

DELETEhttps://api.replen.it/orders/{tenantId}/{orderId}/items
Delete a single line item within an order. Pass productId and sku as query parameters.
Query paramTypeDescription
productIdstringRequiredThe product ID of the line item to remove.
skustringRequiredThe SKU of the specific variant to remove.
curl
curl -X DELETE "https://api.replen.it/orders/YOUR_TENANT_ID/O-789/items?productId=P-42&sku=SKU-42-RED-L" \
  -H "x-replenit-auth-key: YOUR_API_KEY"

POST /products/{tenantId}

POSThttps://api.replen.it/products/{tenantId}
Sync your product catalogue including all variants. Each product must include a ProductVariants array. After ingestion, the reasoning layer automatically processes each product. See Product enrichment.

Request body: array of product objects

Product-level fields(6 fields)
FieldTypeDescription
ProductIdstringRequiredUnique product identifier. Upsert key. Must match ProductId values used in OrderItems.
ProductNamestringRequiredHuman-readable product name. Used in the decision payload and message content.
ProductVariantsarrayRequiredArray of variant objects. A product with no distinct variants should still include one variant for the base product.
Taxonomyarray<string>OptionalOrdered category path, broadest to most specific, e.g. ["Apparel","Women","Activewear"]. Used for category-level targeting.
GenderstringOptionalTarget gender for the product. Gives the reasoning layer an additional audience signal for enrichment and decision targeting.
IsOfflineExclusivebooleanOptionalWhen true, only available in-store. Replenit adapts decision context accordingly. Defaults to false.
Variant fields(13 fields per variant)
FieldTypeDescription
VariantIdstringRequiredUnique variant identifier. Upsert key within a product.
SkustringRequiredMust exactly match the Sku used in OrderItems. Case-sensitive.
NamestringRequiredVariant display name shown in the decision payload.
OriginalPricenumberRequiredFull retail price. Shown even when a sale price is active.
CurrencystringRequiredISO 4217 code for OriginalPrice and SalePrice.
IsAvailablebooleanRequiredtrue if purchasable online. When false, this variant is excluded from decision generation.
ImagestringOptionalPublic HTTPS image URL. Min 600×600px recommended.
SizestringOptionalSize or volume, e.g. 500ml, XL, 100g.
StockintegerOptionalCurrent inventory count.
SalePricenumberOptionalPromotional price. When lower than OriginalPrice, Replenit highlights the saving in the decision payload.
LanguagestringOptionalBCP 47 tag for this variant's content locale. Matched to the customer's Language field.
BrandNamestringOptionalBrand name for this variant. Included in the decision payload.
DescriptionstringOptionalFree-text description of the variant. Every additional descriptive field gives the reasoning layer more signal; richer descriptions improve enrichment precision.

Example request

json
[
  {
    "ProductId": "P-42",
    "ProductName": "Women's Training Tank Top",
    "Taxonomy": ["Apparel", "Women", "Activewear", "Tops"],
    "Gender": "Women",
    "IsOfflineExclusive": false,
    "ProductVariants": [
      {
        "VariantId": "V-42-BLK-S",
        "Sku": "SKU-42-BLK-S",
        "Name": "Black / Small",
        "Description": "Lightweight racerback training tank in breathable recycled mesh.",
        "Image": "https://cdn.example.com/products/tank-black-s.jpg",
        "Size": "S",
        "Stock": 84,
        "OriginalPrice": 34.99,
        "SalePrice": 27.99,
        "Currency": "USD",
        "IsAvailable": true,
        "Language": "en-US",
        "BrandName": "Acme Active"
      }
    ]
  }
]

Code examples

curl
curl -X POST "https://api.replen.it/products/YOUR_TENANT_ID" \
  -H "Content-Type: application/json" \
  -H "x-replenit-auth-key: YOUR_API_KEY" \
  -d @products.json

Response

Returns200400403

Branch on data.rejectedCount, not the HTTP status. See Response format for the full receipt.

json (200)
{
  "success": true,
  "message": "Products saved.",
  "data": {
    "count": 4,
    "accepted": [
      { "index": 0, "recordRef": "productId=P-SMK-1001" },
      { "index": 1, "recordRef": "productId=P-SMK-1002" },
      { "index": 2, "recordRef": "productId=P-SMK-1003" },
      { "index": 3, "recordRef": "productId=P-SMK-1004" }
    ],
    "acceptedCount": 4,
    "rejected": [],
    "rejectedCount": 0,
    "warnings": [],
    "schemaVersion": "Product/v1",
    "schemaSource": "GlobalTemplate",
    "schemaHash": null,
    "processedAt": "2026-09-02T16:17:03.5026951Z"
  }
}

A record that fails an enforced rule — here a variant missing variantId — is rejected individually. The request still returns HTTP 200.

json (200 · record rejected)
{
  "success": true,
  "message": "Products saved.",
  "data": {
    "count": 1,
    "accepted": [],
    "acceptedCount": 0,
    "rejected": [
      {
        "index": 0,
        "recordRef": "productId=SMK-DOC-REJ",
        "reason": "schema_enforce",
        "violations": [
          {
            "path": "productVariants[0].variantId",
            "rule": "required",
            "mode": "Enforce",
            "detail": null
          }
        ]
      }
    ],
    "rejectedCount": 1,
    "warnings": [
      {
        "path": "taxonomy",
        "rule": "required",
        "mode": "Warn",
        "occurrences": 1,
        "sampleIndexes": [0]
      }
    ],
    "schemaVersion": "Product/v1",
    "schemaSource": "GlobalTemplate",
    "schemaHash": null,
    "processedAt": "2026-09-07T13:33:41.8504594Z"
  }
}

DELETE /products/{tenantId}/{productId}

DELETEhttps://api.replen.it/products/{tenantId}/{productId}
Delete a product and all its variants. Cascades: removing the parent removes all child variant records. Asynchronous.
curl
curl -X DELETE "https://api.replen.it/products/YOUR_TENANT_ID/P-42" \
  -H "x-replenit-auth-key: YOUR_API_KEY"

DELETE /products/{tenantId}/{productId}/variants/{variantId}

DELETEhttps://api.replen.it/products/{tenantId}/{productId}/variants/{variantId}
Delete a single product variant without affecting the parent product or other variants. Asynchronous.
curl
curl -X DELETE "https://api.replen.it/products/YOUR_TENANT_ID/P-42/variants/V-42-BLK-S" \
  -H "x-replenit-auth-key: YOUR_API_KEY"

Product enrichment

After a product is ingested, Replenit's reasoning layer automatically analyses each product record and generates a structured set of internal semantic attributes. You do not send these attributes. They are produced by Replenit and used exclusively by the decision engine.

How it works

1. Ingest
Raw product data
Your ProductName, Taxonomy, variant attributes, and pricing are received and stored as the source of record for the reasoning layer.
2. Reason
Semantic interpretation
A reasoning layer reads the product data and interprets it, inferring category, audience, use case, brand positioning, and product-type-specific characteristics not explicitly present in the raw fields.
3. Enrich
Structured attributes
A structured set of semantic attributes is attached to the product record internally, covering subcategory, audience targeting, purchase intent, use case, and physical characteristics.
4. Inform decisions
Decision engine input
Enriched product attributes are passed into Maestro as contextual input when running the Cross-Sell and Replenishment agents, matching the right products to the right customers at the right time.

Enrichment runs automatically after every product upsert. The more complete your product data (particularly ProductName, Taxonomy, BrandName, Gender, and descriptive variant Name and Description fields), the more precise the output.

i
Enrichment attributes are internal to Replenit. They are not returned via the API and are not exposed in the dashboard schema. Their sole purpose is to improve the quality of decisions made by Maestro's agents.
+
Send complete product records, even for optional fields. Every additional data point (Taxonomy, BrandName, Gender, descriptive variant Name and Description, Size, Language) gives the reasoning layer more signal. The more complete your product data, the more precise the output and the better the decisions downstream.

Customer enrichment

Replenit transforms customer transactions into continuously evolving behavioural state. Rather than relying on static scores, segments, or predefined rules, the platform continuously interprets purchasing behaviour over time, identifying intent, behavioural shifts, consumption patterns, and lifecycle signals as customer behaviour evolves.

Each new interaction updates the customer's contextual state, allowing downstream decision systems to reason over evolving behaviour rather than isolated transactions or historical scores.

How it works

1. Ingest
Behavioural history
Every order contributes to a chronological behavioural record: what was purchased, in what combination, at what intervals, and in what sequence. The system treats timing, repetition, and purchase transitions as core behavioural signals, not just transactional totals.
2. Reason
Behavioural interpretation
A reasoning layer continuously interprets the customer's evolving behavioural state to identify contextual patterns and intent signals that rigid rules and static scoring systems fail to capture, reasoning over purchasing rhythm changes, category expansion, premiumisation, gifting behaviour, lifecycle transitions, household dynamics, and emerging intent.
3. Enrich
Semantic context
Products and transactions are enriched with contextual metadata (category relationships, brand positioning, replenishment behaviour, pricing dynamics, audience signals, and consumption patterns), composed into a continuously evolving customer context.
4. Inform decisions
Contextual input to Maestro
The interpreted behavioural state is passed into Maestro and downstream decision agents as contextual input. Decisions are generated based on behavioural momentum, lifecycle state, intent, and evolving preferences, not static segment-level probabilities or predefined campaign logic.
i
Customer enrichment is a continuously evolving profile, not a snapshot. It is re-evaluated as new orders arrive. There is no batch schedule. The interpreted context is internal to Replenit and is not returned via the API.
+
Send historical orders at integration time. The reasoning layer produces more accurate and nuanced behavioural profiles with more history to work from. An initial bulk load of 12–24 months of order history is strongly recommended to avoid a cold-start period.

Error codes

StatusMeaningAction
200SuccessRequest processed. Continue normal flow.
400Malformed requestThe request itself could not be parsed. Returns an RFC 9110 ProblemDetails object, not the standard envelope
403Access deniedVerify the x-replenit-auth-key header and key value, and that the URL path uses your tenant GUID. Body is plain text, not JSON
404Tenant not foundVerify tenant ID is the correct GUID from Settings → API
429Rate limitedReduce request rate; use Retry-After header; implement exponential backoff
500Server errorRetry with backoff. If persistent, contact support@replen.it
!
Error responses are not always JSON. A 400 returns a ProblemDetails object and a 403 returns plain text, so do not call response.json() unconditionally. Check the status code and the Content-Type header first.

400 — malformed request

A 400 now means the request itself could not be parsed: the body is not valid JSON, or it is not an array. Per-record validation failures no longer surface as 400 — a well-formed request containing invalid records returns 200 with those records listed under rejected.

json (400)
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "$": ["Expected depth to be zero at the end of the JSON payload. …"]
  },
  "traceId": "00-497e1ed30e2c870038bc415e729d76f9-92fa48c278c1c812-00"
}

403 — access denied

A missing or invalid auth key returns 403 with a plain-text body:

text (403)
RBAC: access denied
i
The same 403 is returned when the URL path uses your tenant name instead of the tenant GUID. The request is denied at the gateway, with nothing in the response to indicate that the tenant identifier was the problem. If your key is correct and you are still seeing RBAC: access denied, check that {tenantId} is the GUID from Settings → API.

Rate limits

Standard tier: 100 requests/minute, 5,000 requests/hour. All responses include rate limit headers.

response headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000
Retry-After: 30 // present on 429 responses

Best practices

!
Handle the ingestion receipt. Every POST returns HTTP 200 with "success": true as long as the request was well formed, even when records inside it were rejected. Branch on data.rejectedCount, log the violations of each rejected record so the failing fields are visible in your own system, and never treat the status code alone as confirmation that the batch was stored.
+
Batch your requests. Send arrays of up to 500 records per call (100–200 for orders and products). Batching dramatically reduces API overhead for initial historical loads.
+
Send products before orders. Product records should exist before OrderItems reference their SKUs. Missing SKU references won't fail the order ingest, but the reasoning layer won't be able to contextualise those line items until the product is present.
+
Keep channel availability flags current. Always send the latest values for EmailOptin, SmsOptin, WhatsappOptin, and AppPushOptin. Replenit uses these as behavioural profiling signals. Stale values will skew how decisions are contextualised for that customer.
+
Update inventory in real time. Sync Stock and IsAvailable whenever inventory changes. When IsAvailable is false, that variant will not be included in decisions. Keeping this current prevents decisions being generated for products that cannot be fulfilled.
+
Send complete records, even for optional fields. Every additional data point (Taxonomy, BrandName, Gender, variant Name, Description, Size, Language) gives the reasoning layer more signal to work with. The more complete your records, the more precise and well-contextualised the decisions Replenit generates.
i
Only send consented customers to Replenit. GdprOptin defaults to false. Replenit will not process, enrich, or generate decisions for any customer where this is false. Sending only customers with valid, current consent is a legal obligation that must be met in your systems before data reaches this API. Replenit does not validate, manage, or refresh consent state. Message-level suppression at delivery time is handled by your connected marketing automation platform.
!
Send the identifier your destination can resolve. Before integrating, confirm whether your marketing automation platform or data platform matches incoming decision events on email address or customer ID. If you send only CustomerId and your destination cannot resolve it, the decision will arrive but cannot be matched to a customer record. When in doubt, send both CustomerId and Email on every customer record.
!
Match SKUs exactly. The Sku in OrderItems must exactly match a ProductVariants[].Sku, case-sensitive. Mismatches break the link between purchase history and the product catalogue, degrading reasoning quality.
!
ISO 8601 dates only. All OrderDate values must be UTC ISO 8601: 2025-08-22T21:00:00Z. Records carrying localised date strings are rejected individually and appear in data.rejected.
!
Request body must be an array. All POST endpoints expect a JSON array, even for a single record: [{...}] not {...}. This is the most common cause of 400 errors.
Need a hand?

Talk to the integration team

Stuck on a payload, an SKU mapping, or production limits? Reach our engineers directly and we'll get you unblocked.