> ## Documentation Index
> Fetch the complete documentation index at: https://docs.upwell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# What happens if…?

> The consequence reference: what the system actually does when you retry, duplicate, delete, paginate, or misconfigure — one answer per question, with sources.

# What happens if…?

Docs usually tell you the happy path. This page tells you what happens down the *other* branch —
the retry, the duplicate, the delete, the misconfigured receiver. Each answer links its source.
**\[GAP]** marks the questions the documentation cannot answer yet; if one blocks you, ask
[support@upwell.com](mailto:support@upwell.com).

## Writes and retries

<AccordionGroup>
  <Accordion title="…I POST the same customer / shipment / invoice twice?">
    The second `POST` fails with `400 Uniqueness violation` on the
    `(sourceSystem, sourceSystemId)` pair. First sync is `POST`, every later change is `PUT`.
    Source: [Idempotency & retries](/conventions/idempotency-and-retries).
  </Accordion>

  <Accordion title="…I POST the same carrier twice?">
    It **silently succeeds and changes nothing** — a no-op upsert that does not update `name`,
    `email`, `phone`, or anything else you sent. If you meant to update, use `PUT`. Source:
    [Syncing foundation entities](/api-guides/syncing-foundation-entities).
  </Accordion>

  <Accordion title="…I POST the same carrier invoice twice (e.g. a retry after a timeout)?">
    Both inserts succeed and return **different ids**. Seconds later, an async dedup pass
    soft-deletes the **newer** row, keyed on `(tenant, sourceSystemId)` — note: not scoped by
    integration, so two integrations reusing id strings collide. The id your retry returned may
    be the deleted one; reconcile by looking the invoice up by `sourceSystemId` afterward.
    Source: [Re-submitting](/api-guides/carrier-invoice-submission#re-submitting). One caveat on
    the lookup itself — see [filtering a list](#reading-data-back) below.
  </Accordion>

  <Accordion title="…I re-request a presigned upload URL for the same document pair?">
    Fails fast with `400 DOCUMENT_ALREADY_UPLOADED` — synchronous, unlike carrier invoices.
    Exception: re-requesting one whose upload never completed returns a fresh URL for the same
    document. Source: [Purchase orders & vendor invoices](/api-guides/purchase-orders-and-vendor-invoices).
  </Accordion>

  <Accordion title="…a duplicate sneaks into a bulk batch?">
    `bulk-carriers`: the **entire batch fails** (no upsert). `bulk-bills`: upserts like its
    singular endpoint. Neither reports partial success. What the failure response tells you
    about *which* row broke the batch: **\[GAP]**. Source:
    [Syncing foundation entities](/api-guides/syncing-foundation-entities).
  </Accordion>

  <Accordion title="…I send money as dollars-and-cents floats?">
    Runtime rejection: `"The value 2847.5 lies outside the bounds or is not an integer."` All
    monetary fields are integer cents. Source: [Money & payloads](/conventions/money-and-payloads).
  </Accordion>

  <Accordion title="…I omit a required field that has no server default?">
    `400` with the column name in a flat error string, e.g. `"Not-NULL violation. null value in
            column \"shipment_id\"…"`. Known cases: `shipments.shipmentId`, `invoices.balance` — the
    OpenAPI spec marks these `required`, so generated clients catch them before the wire.
    Source: [Integration patterns](/integration-patterns).
  </Accordion>
</AccordionGroup>

## Errors, limits, and the reference

<AccordionGroup>
  <Accordion title="…a call fails — what shape is the error?">
    A **flat object**: `{"error": "<message>"}`, sometimes with a `code` such as
    `constraint-violation` or `bad-request`. An invalid or missing API key returns `401` with an
    **empty body**. There is no nested error envelope. The message→fix table lives in
    [Error handling](/api-reference/error-codes).
  </Accordion>

  <Accordion title="…I exceed the rate limit?">
    There is currently **no rate limiting** on the API — no `429`, no `X-RateLimit-*` headers,
    no `Retry-After`. A `429` you observe came from your own proxy or gateway. Design your
    client politely (bounded concurrency, backoff on `5xx`), but don't build 429 machinery.
    Source: [Error handling](/api-reference/error-codes).
  </Accordion>

  <Accordion title="…I generate a client from the published OpenAPI spec?">
    It now declares authentication (`ApiKeyAuth` in the `Authorization` header), the
    machine-enforced required fields, the real UPPER\_SNAKE\_CASE status enums, the nested
    `invoiceShipments` insert, and generic `400`/`401` responses. One remaining caveat: the
    `where`/`orderBy` filters that some list `GET`s accept are **not** in the spec — pending
    verification of the query-string transport. **\[GAP — verification in progress]** Prefer the
    `POST /search` endpoints for filtering.
  </Accordion>
</AccordionGroup>

## Reading data back

<AccordionGroup>
  <Accordion title="…I GET a list endpoint with no parameters?">
    You get the **first 10 records** — the default `limit` is 10 (a handful of endpoints default
    to 100). Nothing marks the response as truncated. Always pass `limit`/`offset` explicitly.
    Source: [API conventions](/api-reference/introduction).
  </Accordion>

  <Accordion title="…I need to filter a list server-side?">
    Three cases:

    1. `POST /<resource>/search` exists for customers, carriers, shipments, shipment line items,
       invoices, bills, bill payments, customer payments (+ line items), vendors, purchase
       orders, vendor invoices — use it ([Integration patterns](/integration-patterns)).
    2. Addresses and companies have **no** search — page the collection or go through the parent.
    3. A `?where=` JSON filter on list GETs: the underlying queries accept it, but whether the
       GET query-string transport parses object-typed params is still being verified —
       **\[GAP — verification in progress]**. Until it's confirmed, use `POST /search` where it
       exists.
  </Accordion>

  <Accordion title="…I try to read back an address I created nested under a company?">
    You can't get its id from the create response (`POST /api/rest/companies` returns no address
    ids), companies expose no address read path, and addresses carry no external key — the row
    is recoverable only by paging `GET /api/rest/addresses`. Create company addresses standalone
    instead. Source: [Working with addresses](/conventions/addresses).
  </Accordion>
</AccordionGroup>

## Deletes

<AccordionGroup>
  <Accordion title="…I DELETE a customer?">
    **Hard delete**, and its addresses go with it — and per the
    [AR overview](/api-reference/ar-overview), its invoices too. The external key pair is freed.
    Prefer the reversible soft delete (`PUT` with `deletedAt`), which frees the key equally.
    A dedicated "deactivate" mechanism beyond soft delete is not documented. **\[GAP]** Source:
    [Deleting and archiving](/conventions/deletes-and-archiving).
  </Accordion>

  <Accordion title="…I DELETE a company?">
    **Soft delete** — the record disappears from the app, its (case-insensitively unique) name
    is freed, and its addresses are orphaned rather than removed. Source:
    [Deleting and archiving](/conventions/deletes-and-archiving).
  </Accordion>

  <Accordion title="…I DELETE a vendor that still has bills?">
    It fails outright (restrict-on-delete). Reassign or remove the bills first. Vendor deletes
    are permanent — no soft-delete column. Source:
    [Syncing foundation entities](/api-guides/syncing-foundation-entities).
  </Accordion>

  <Accordion title="…I DELETE a rule I might want back?">
    Gone permanently — rules never soft-delete. Set `enabled: false` instead. Source:
    [Rules](/conventions/rules).
  </Accordion>
</AccordionGroup>

## Webhooks misbehaving

<AccordionGroup>
  <Accordion title="…my receiver returns 5xx or times out?">
    The delivery retries — `5xx`, timeouts, and network failures all count — up to the
    subscription's `retryAttempts`, with exponential backoff (1 min, 2 min, 4 min, …).
    `retryAttempts: 3` (the default) means up to **4 total deliveries**. Each attempt has a
    15-second timeout — ack fast, work async. Source:
    [Outbound webhooks](/api-guides/webhooks).
  </Accordion>

  <Accordion title="…my receiver returns 4xx (bad auth config, wrong route)?">
    **The event is recorded as delivered and never retried.** This is the silent-loss case:
    Upwell's side shows success while your side rejected everything. Alert on your receiver's
    4xx rate; reconcile by polling. Source: [Outbound webhooks](/api-guides/webhooks),
    Reliability.
  </Accordion>

  <Accordion title="…I miss deliveries entirely (outage on my side)?">
    After retries exhaust, there is no automatic replay and no delivery-inspection surface —
    recovery is polling the resource endpoints (`GET /carrier_invoices/{id}` etc.), which the
    [status guide](/api-guides/carrier-invoice-status) recommends running periodically anyway.
    Source: [Outbound webhooks](/api-guides/webhooks), Reliability.
  </Accordion>

  <Accordion title="…I subscribe to update.carrier_invoice expecting every change?">
    Each change fires **exactly one** trigger — the most specific that applies. A tracked status
    transition (e.g. to `APPROVED`) fires only `update.carrier_invoice.status.APPROVED`, *not*
    also the generic `update.carrier_invoice`. Subscribe to every trigger you care about.
    Source: [Webhook event catalog](/api-guides/webhook-events).
  </Accordion>

  <Accordion title="…I want carrier-document or customer-invoice-APPROVED events?">
    Those four triggers (`create/update/delete.carrier_document`,
    `update.invoice.status.APPROVED`) exist in the dispatcher but are **not self-service** —
    they're marked † in the [event catalog](/api-guides/webhook-events); contact support to
    enable them.
  </Accordion>

  <Accordion title="…a customer/carrier/shipment changes inside Upwell?">
    No webhook fires — foundation entities have no triggers by design; your system remains the
    system of record and reads state back by polling. Source:
    [Webhook event catalog](/api-guides/webhook-events).
  </Accordion>
</AccordionGroup>

## Lifecycle surprises

<AccordionGroup>
  <Accordion title="…my new carrier invoice immediately shows exceptions?">
    Expected. A brand-new row lists matching findings ("no matching shipment / bill") that clear
    on their own when ingestion completes. Wait for `updatedAt > createdAt` before judging
    `exceptions`. Source: [Carrier invoice status](/api-guides/carrier-invoice-status).
  </Accordion>

  <Accordion title="…an invoice sits in a status my table doesn't list?">
    The carrier-invoice status enum has 16 values; the docs' tables define 13. `AWAITING_BILL`,
    `CANCELLED`, and `REFUNDED` are currently undefined in prose. **\[GAP — definitions needed]**
  </Accordion>

  <Accordion title="…an approval request is resolved FAILED?">
    The invoice is rejected — but which status it lands in (`REJECTED`? `EXCEPTION`?) is not
    documented. **\[GAP]** Source: [How approvals work](/concepts/approvals),
    [Responding to approval requests](/api-guides/carrier-invoice-approval).
  </Accordion>

  <Accordion title="…a clean invoice isn't auto-approving?">
    Auto-approval runs on a schedule, not instantly; it only touches invoices with no open or
    in-progress exceptions, matched to a bill and carrier, inside your guardrails — and the
    master switch is off by default. The cadence is not published. **\[GAP — cadence]** Source:
    [How approvals work](/concepts/approvals).
  </Accordion>

  <Accordion title="…I apply a customer payment via the API — does the invoice update itself?">
    No. Creating payment line items does not recalculate the target invoice's
    `balance`/`status`; your integration updates the invoice afterward. (Upwell's own internal
    payment pipelines do update invoices — which is why payments recorded inside Upwell behave
    differently.) Source: [Customer invoices](/api-guides/customer-invoices) (Tip),
    [Remittances](/accounts-receivable/remittances).
  </Accordion>

  <Accordion title="…I upload a rate confirmation with the wrong documentType?">
    It is parsed and stored as what you labeled it — a carrier rate con typed
    `CUSTOMER_RATE_CONFIRMATION` pollutes customer-side validation and vice versa. Re-type and
    re-process rather than editing amounts; never let carrier cost reach the customer side.
    Source: [Document conventions](/conventions/documents).
  </Accordion>

  <Accordion title="…I save an address that never shows up in the app?">
    Almost always a `type` problem: pages render only specific types (`CUSTOMER_BILLING`,
    `COMPANY_ADDRESS`), and `ADDRESS_BOOK` is read by nothing. The write succeeded; the page
    filtered it out. Debug order and per-page field lists:
    [Working with addresses](/conventions/addresses).
  </Accordion>
</AccordionGroup>
