Markup error codes — full catalog
Every typed error the markup pipeline can return, with cause, fix, and the exact JSON envelope shape so an SDK or agent can switch on `code`.
Every Sendero JSON route raises errors via the standard envelope:
The code is the stable API contract. message and agentInstruction may evolve; the code never silently changes meaning.
POLICY_INACTIVE
HTTP: 412 (CRUD route returns 409 — both are intentional; the tool surface uses 412 "precondition failed" because the policy precondition for confirm hasn't been met).
Cause: the tenant has no TenantPricingPolicy row with activated = true. Either no row exists, or only the sandbox seed row exists.
Fix: the tenant admin publishes a policy at /dashboard/settings/pricing, OR an admin agent calls activate_tenant_pricing_policy.
POLICY_PARTIAL_FOR_KIND
HTTP: 412.
Cause: the policy is activated but markupConfig doesn't define a row for the booking kind being confirmed (e.g. tenant set up flight and hotel but the agent is confirming a rail booking).
Fix: add the missing kind to the policy. Re-publishing inserts a new policy version; in-flight quotes already snapshotted the old version and continue to fail until they're re-quoted.
TREASURY_NOT_PROVISIONED
HTTP: 409 on activate, 412 on confirm (TREASURY_ADDRESS_MISSING).
Cause: the tenant's CircleWallet.address is unset or the zero address. Without a payout address the agency leg of commitBookingV2 would revert.
Fix: wait a few seconds for the organization.created Clerk webhook to finish, or hit /api/tenant/wallet/sync to force a re-provision.
MARKUP_OVER_CEILING
HTTP: 422.
Cause: the computed markupMicroUsdc exceeds the tenant's self-set ceilingMicroUsdc. Either the agent passed an explicit markupBps / markupMicroUsdc higher than the ceiling, or the policy default itself overshoots after re-evaluation.
Fix: lower the markup, raise the ceiling at /dashboard/settings/pricing, or supply a privileged override (see markup-scopes).
MARKUP_UNDER_FLOOR
HTTP: 422.
Cause: the computed markupMicroUsdc is below the tenant's self-set floorMicroUsdc. Common when an agent explicitly overrides with a small number.
Fix: raise the markup, or lower the floor at /dashboard/settings/pricing.
MARKUP_UNDER_TAKE_FLOOR
HTTP: 422.
Cause: in senderoTakeBehavior: 'deduct_from_markup' mode, the Sendero take exceeds the agency markup — the agency leg would clamp to zero. The tool refuses to settle this rather than silently produce a $0 agency leg.
Fix: raise the markup, switch the policy to add_to_customer (the customer absorbs the fee instead), or pick a smaller booking.
MARKUP_AMBIGUOUS_INPUT
HTTP: 400.
Cause: both markupBps and markupMicroUsdc were passed on the same confirm_booking call. They're mutually exclusive — pick one.
Fix: drop one of the fields.
OVERRIDE_REQUIRES_SCOPE
HTTP: 403.
Cause: the caller passed override.acknowledgedMicroUsdc to bypass the ceiling, but the API key does not carry the tenant:pricing:override scope (or is a sandbox key — sandboxes are blocked unconditionally even with '*').
Fix: mint a production API key with the override scope from /dashboard/settings/api-keys, OR drop the override and price within the ceiling. See markup-scopes.
OVERRIDE_UNNECESSARY
HTTP: 400.
Cause: the caller passed override but the markup is already within the ceiling. Treated as a code smell — we don't want sandbox runs silently exercising the privileged path.
Fix: drop the override field.
MARKUP_CONFIG_INVALID
HTTP: 422.
Cause: the markupConfig payload to activate_tenant_pricing_policy (or the CRUD route) failed Zod validation. v1 only honors { strategy: 'static', bps: <int 0..10000> }. v2-style strategies (e.g. tiered, volume_curve) parse but throw MarkupStrategyNotSupportedV1 at confirm time.
Fix: fix the offending kind and retry. The details.zodIssues field surfaces the per-field issues.
Switching on code — recommended SDK shape
The traceId is the one field every support ticket should quote — we key our audit log on it.