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:
34.12.230.237 34.91.210.3
All outbound requests from Replenit’s production agent infrastructure originate from these IP addresses.
- Expected request volume and traffic patterns
- User-agent details
- Target domains and URL paths
- Request frequency and concurrency
- Environment-specific access requirements
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.
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.
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.
Retrieve both from your dashboard under Settings → API Keys.
POST https://api.replen.it/customers/{tenantId} x-replenit-auth-key: YOUR_API_KEY Content-Type: application/json
Base URL & endpoints
| Endpoint | Method | Purpose | Batch |
|---|---|---|---|
| /customers/{tenantId} | POST | Upsert customer profiles | 100–500 |
| /customers/{tenantId} | DELETE | Delete customer by ID or email | Single record |
| /orders/{tenantId} | POST | Upsert orders with line items | 50–200 |
| /orders/{tenantId}/{orderId} | DELETE | Delete an entire order | Single record |
| /orders/{tenantId}/{orderId}/items | DELETE | Delete a single line item | Single record |
| /products/{tenantId} | POST | Upsert product catalogue | 50–100 |
| /products/{tenantId}/{productId} | DELETE | Delete product and all variants | Single record |
| /products/{tenantId}/{productId}/variants/{variantId} | DELETE | Delete a single variant | Single record |
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.
Internal pipeline
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.
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
| Field | Type | Meaning | |
|---|---|---|---|
| count | integer | Number of records read from the request body. | |
| accepted | array | One entry per accepted record: { index, recordRef }. | |
| acceptedCount | integer | Length of accepted. | |
| rejected | array | One entry per rejected record, each carrying index, recordRef, reason, and a violations array naming the fields that failed. | |
| rejectedCount | integer | Length of rejected. This is the field your integration should branch on. | |
| warnings | array | Non-blocking rule findings, aggregated per rule across the whole batch rather than per record. | |
| schemaVersion | string | Version identifier of the validation schema that processed the request, e.g. Product/v1. | |
| schemaSource | string | Origin of the validation schema. | |
| schemaHash | string | null | Integrity hash of the validation schema, or null. | |
| processedAt | string | UTC 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
{
"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.
{
"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.
{
"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.
warningsare aggregated per rule across the whole batch, withoccurrences,sampleIndexes, and a template-formpathsuch asproductVariants[].currency.violationsare per record with a concrete index, e.g.productVariants[0].variantId. The two cannot be joined onpath. detailcan 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.recordRefcan be null. When the missing field is the identifier itself, there is nothing to name the record by. Fall back toindexto map the entry back to your request array.
POST /customers/{tenantId}
CustomerId or Email is the upsert key. At least one is required.Request body: array of customer objects
CustomerId or Email is required. They can be sent individually or together. Send whichever identifier your dispatch destination can resolve. See the note below.| Field | Type | Description | |
|---|---|---|---|
| CustomerId | string | Required* | Your unique customer identifier. Max 100 chars. Upsert key. Sending the same value updates the existing record. Required if not sending Email. |
| string | Required* | Valid email address. Upsert key when CustomerId is absent. Required if not sending CustomerId. | |
| Name | string | Optional | First name. Included in the decision payload for personalisation at the execution layer. |
| Surname | string | Optional | Last name / surname. |
CustomerId but your destination cannot resolve it, decisions arrive but cannot be matched to a record. When in doubt, send both.| Field | Type | Description | |
|---|---|---|---|
| Phone | string | Optional | E.164 format, e.g. +14155550123. Profiling signal; passed in the decision payload to downstream platforms. |
| Language | string | Optional | BCP 47 tag, e.g. en-US, es-ES. Used by the reasoning layer to contextualise decisions and match localised product content. |
| Username | string | Optional | Platform username. Included in the decision payload for personalisation at the execution layer. |
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.
| Field | Type | Description | |
|---|---|---|---|
| EmailOptin | boolean | Optional | Customer is reachable via email. |
| SmsOptin | boolean | Optional | Customer is reachable via SMS. |
| WhatsappOptin | boolean | Optional | Customer is reachable via WhatsApp. |
| AppPushOptin | boolean | Optional | Customer is reachable via mobile push notification. |
| Field | Type | Description | |
|---|---|---|---|
| GdprOptin | boolean | Optional | Controls 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. |
| IsExcluded | boolean | Optional | When true, this customer is excluded from the decision pipeline entirely. Use for internal test accounts. Defaults to false. |
Example request
[
{
"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 -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}]'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();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
Branch on data.rejectedCount, not the HTTP status. See Response format for the full receipt.
{
"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}
customerId or email. Pass either as a query parameter. Asynchronous, queued and applied idempotently.| Query param | Type | Description | |
|---|---|---|---|
| customerId | string | Optional | Delete by customer ID. Use this or email. |
| string | Optional | Delete by email address. Use this or customerId. |
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}
OrderItems array containing at least one item.Request body: array of order objects
| Field | Type | Description | |
|---|---|---|---|
| OrderId | string | Required | Unique order identifier. Upsert key: resend to update (e.g. cancellations). |
| CustomerId | string | Required | Links to the customer's CustomerId. |
| string | Required | Customer email. Used as a fallback if CustomerId is not matched. | |
| OrderDate | string | Required | ISO 8601 UTC datetime, e.g. 2025-08-22T21:00:00Z. Baseline for replenishment timing. |
| TotalRevenue | number | Required | Total order value as a decimal in the order's currency. |
| Currency | string | Required | ISO 4217 code, e.g. USD, EUR, GBP. |
| OrderItems | array | Required | Array of line item objects. Minimum one item required. |
| TotalQuantity | integer | Optional | Total units across all line items. |
| UniqueQuantity | integer | Optional | Number of distinct product SKUs in the order. |
| IsOrderCancelled | boolean | Optional | Set to true for cancelled orders. Excluded from timing models. Defaults to false. |
| Field | Type | Description | |
|---|---|---|---|
| ProductId | string | Required | Links to the product's ProductId. |
| Sku | string | Required | Must exactly match a ProductVariants[].Sku. Case-sensitive. |
| Quantity | integer | Required | Units purchased. Used to calculate depletion rate. |
| Price | number | Required | Actual price paid per unit after discounts. |
| OriginalPrice | number | Optional | Full retail price before discounts. |
| Size | string | Optional | Size or volume, e.g. 500ml, XL. |
| Color | string | Optional | Colour of the variant purchased. |
| Brand | string | Optional | Brand name of the item. |
| IsGift | boolean | Optional | When true, item was a gift. Replenit adjusts replenishment timing accordingly. Defaults to false. |
Example request
[
{
"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 -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
Branch on data.rejectedCount, not the HTTP status. See Response format for the full receipt.
{
"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}
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
productId and sku as query parameters.| Query param | Type | Description | |
|---|---|---|---|
| productId | string | Required | The product ID of the line item to remove. |
| sku | string | Required | The SKU of the specific variant to remove. |
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}
ProductVariants array. After ingestion, the reasoning layer automatically processes each product. See Product enrichment.Request body: array of product objects
| Field | Type | Description | |
|---|---|---|---|
| ProductId | string | Required | Unique product identifier. Upsert key. Must match ProductId values used in OrderItems. |
| ProductName | string | Required | Human-readable product name. Used in the decision payload and message content. |
| ProductVariants | array | Required | Array of variant objects. A product with no distinct variants should still include one variant for the base product. |
| Taxonomy | array<string> | Optional | Ordered category path, broadest to most specific, e.g. ["Apparel","Women","Activewear"]. Used for category-level targeting. |
| Gender | string | Optional | Target gender for the product. Gives the reasoning layer an additional audience signal for enrichment and decision targeting. |
| IsOfflineExclusive | boolean | Optional | When true, only available in-store. Replenit adapts decision context accordingly. Defaults to false. |
| Field | Type | Description | |
|---|---|---|---|
| VariantId | string | Required | Unique variant identifier. Upsert key within a product. |
| Sku | string | Required | Must exactly match the Sku used in OrderItems. Case-sensitive. |
| Name | string | Required | Variant display name shown in the decision payload. |
| OriginalPrice | number | Required | Full retail price. Shown even when a sale price is active. |
| Currency | string | Required | ISO 4217 code for OriginalPrice and SalePrice. |
| IsAvailable | boolean | Required | true if purchasable online. When false, this variant is excluded from decision generation. |
| Image | string | Optional | Public HTTPS image URL. Min 600×600px recommended. |
| Size | string | Optional | Size or volume, e.g. 500ml, XL, 100g. |
| Stock | integer | Optional | Current inventory count. |
| SalePrice | number | Optional | Promotional price. When lower than OriginalPrice, Replenit highlights the saving in the decision payload. |
| Language | string | Optional | BCP 47 tag for this variant's content locale. Matched to the customer's Language field. |
| BrandName | string | Optional | Brand name for this variant. Included in the decision payload. |
| Description | string | Optional | Free-text description of the variant. Every additional descriptive field gives the reasoning layer more signal; richer descriptions improve enrichment precision. |
Example request
[
{
"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 -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
Branch on data.rejectedCount, not the HTTP status. See Response format for the full receipt.
{
"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.
{
"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}
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}
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
ProductName, Taxonomy, variant attributes, and pricing are received and stored as the source of record for the reasoning layer.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.
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
Error codes
| Status | Meaning | Action |
|---|---|---|
| 200 | Success | Request processed. Continue normal flow. |
| 400 | Malformed request | The request itself could not be parsed. Returns an RFC 9110 ProblemDetails object, not the standard envelope |
| 403 | Access denied | Verify the x-replenit-auth-key header and key value, and that the URL path uses your tenant GUID. Body is plain text, not JSON |
| 404 | Tenant not found | Verify tenant ID is the correct GUID from Settings → API |
| 429 | Rate limited | Reduce request rate; use Retry-After header; implement exponential backoff |
| 500 | Server error | Retry with backoff. If persistent, contact support@replen.it |
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.
{
"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:
RBAC: access denied
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.
X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1640000000 Retry-After: 30 // present on 429 responses
Best practices
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.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.EmailOptin, SmsOptin, WhatsappOptin, and AppPushOptin. Replenit uses these as behavioural profiling signals. Stale values will skew how decisions are contextualised for that customer.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.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.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.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.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.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.[{...}] not {...}. This is the most common cause of 400 errors.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.
