> ## 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.

# Document conventions

> The sourceSystem/sourceSystemId pair rule, the two-step presigned upload (the only path that runs AI classification), and typing rate confirmations correctly.

## Document sourceSystem / sourceSystemId pair constraint

On `documents`, the check constraint `documents_source_system_pair_nullity_chk` requires both fields to be set or both to be null. Setting only `sourceSystem` without `sourceSystemId` fails with:

```
"Check constraint violation. ... documents_source_system_pair_nullity_chk"
```

The simplest pattern: always pass a `sourceSystemId` alongside `sourceSystem`. If you don't have a meaningful external id for a document, generate one (e.g. a UUID, or `<your-system>_doc_<timestamp>`).

## Document upload: two-step presigned URL

Use the two-step presigned flow for document uploads — for **carrier-invoice documents it's the only path that runs AI classification**: a combined or `UNKNOWN` PDF sent through an inline-base64 endpoint is filed but never classified or parsed, so nothing populates from it. (The base64 endpoints also reject payloads past a few MB.) The flow:

1. `POST /api/rest/generate-upload-presigned-url` with `{ associationType, associationId, documentType, fileName, mimeType, sourceSystem, sourceSystemId }` returns `{ documentId, uploadUrl }`.
2. `PUT` the file bytes directly to `uploadUrl`.

<Tip>
  The presigned URL is signed for `host`-only headers. You can send `Content-Type` on the `PUT`,
  but avoid adding headers that aren't in `X-Amz-SignedHeaders`. Wrapping the body in a `Blob`
  with an explicit type can cause some Node `fetch` implementations to add headers that break the
  signature — passing an `ArrayBuffer` is the safe path. Full upload mechanics:
  [Document conventions](/conventions/documents#document-upload-two-step-presigned-url).
</Tip>

### Rate confirmations: customer vs. carrier

A customer rate confirmation and a carrier rate confirmation are two separate document types in Upwell — separate `documentType` values, separate parsers, and separate storage. **Sending the correct one matters:**

| `documentType`               | What it is                                                       | Used for                                    |
| ---------------------------- | ---------------------------------------------------------------- | ------------------------------------------- |
| `CARRIER_RATE_CONFIRMATION`  | The rate you agreed to **pay the carrier** — carrier cost.       | Carrier-invoice audit (payables).           |
| `CUSTOMER_RATE_CONFIRMATION` | The rate your **customer agreed to pay you** — customer revenue. | Validating customer invoices (receivables). |

Type a carrier rate confirmation as `CARRIER_RATE_CONFIRMATION` and a customer rate confirmation as `CUSTOMER_RATE_CONFIRMATION`. Each is AI-parsed on upload (through the two-step presigned flow above) by its own parser into its own storage, including the total rate. A document typed as one is never parsed, stored, or evaluated as the other.

<Warning>
  Never label a carrier rate confirmation as `CUSTOMER_RATE_CONFIRMATION` (or vice versa). The carrier rate is your cost/margin and must never reach the customer. If a rate confirmation is mis-typed on upload, re-type the document and re-process it rather than editing the parsed amount.
</Warning>
