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

# How Ramp + NetSuite Payments Work

> The end-to-end path an approved Owlery invoice takes to become a paid Vendor Bill in NetSuite — using Ramp for bill pay.

Ramp + NetSuite is one of the most common combinations Owlery shippers use to pay carriers. Owlery handles the freight side (approving carrier invoices), **Ramp** handles the money movement (approving and paying bills), and **NetSuite** stays the system of record for accounting.

The diagram below is the shared picture of the pipeline. Beneath it, "What this means for your accounting team" and "What this means for your IT team" describe the same flow from each team's perspective.

## The flow at a glance

<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.35rem', maxWidth: '560px', margin: '1rem auto' }}>
  <div style={{ width: '100%', boxSizing: 'border-box', padding: '0.9rem 1rem', borderRadius: '12px', background: '#166E3F', color: '#ffffff' }}>
    <div style={{ fontSize: '0.68rem', textTransform: 'uppercase', letterSpacing: '0.08em', opacity: 0.85 }}>Owlery</div>
    <div style={{ fontWeight: 700, marginTop: '0.2rem' }}>Approved invoice</div>
    <div style={{ fontSize: '0.82rem', marginTop: '0.3rem', opacity: 0.9 }}>Carrier invoice approved and sent to accounting.</div>
  </div>

  <div style={{ textAlign: 'center', color: '#94a3b8', fontSize: '0.78rem', lineHeight: 1.3 }}>
    Owlery creates the draft bill
    <div style={{ fontSize: '1.3rem', lineHeight: 1 }}>↓</div>
  </div>

  <div style={{ width: '100%', boxSizing: 'border-box', padding: '0.9rem 1rem', borderRadius: '12px', background: '#F3E8FF', border: '1px solid #7C3AED', color: '#3b1a63' }}>
    <div style={{ fontSize: '0.68rem', textTransform: 'uppercase', letterSpacing: '0.08em', color: '#7C3AED' }}>Ramp</div>

    <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '0.4rem', marginTop: '0.45rem', fontSize: '0.82rem' }}>
      <span style={{ padding: '0.15rem 0.55rem', borderRadius: '999px', background: 'rgba(124,58,237,0.14)' }}>1 · Draft Bill</span>
      <span style={{ color: '#7C3AED' }}>→</span>
      <span style={{ padding: '0.15rem 0.55rem', borderRadius: '999px', background: 'rgba(124,58,237,0.14)' }}>2 · Approved Bill</span>
      <span style={{ color: '#7C3AED' }}>→</span>
      <span style={{ padding: '0.15rem 0.55rem', borderRadius: '999px', background: 'rgba(124,58,237,0.14)' }}>3 · Payment on bill</span>
    </div>
  </div>

  <div style={{ textAlign: 'center', color: '#94a3b8', fontSize: '0.78rem', lineHeight: 1.3 }}>
    Ramp's native NetSuite sync
    <div style={{ fontSize: '1.3rem', lineHeight: 1 }}>↓</div>
  </div>

  <div style={{ width: '100%', boxSizing: 'border-box', padding: '0.9rem 1rem', borderRadius: '12px', background: '#E8F1FF', border: '1px solid #2563EB', color: '#0b2e6b' }}>
    <div style={{ fontSize: '0.68rem', textTransform: 'uppercase', letterSpacing: '0.08em', color: '#2563EB' }}>NetSuite</div>

    <div style={{ marginTop: '0.45rem', fontSize: '0.84rem', lineHeight: 1.7 }}>
      <div><strong>Vendor Bill</strong> — Phase 1 <code style={{ background: 'rgba(37,99,235,0.14)', padding: '0.05rem 0.35rem', borderRadius: '4px' }}>BILL\_SYNC</code></div>
      <div><strong>Bill Payment</strong> — Phase 2 <code style={{ background: 'rgba(37,99,235,0.14)', padding: '0.05rem 0.35rem', borderRadius: '4px' }}>BILL\_PAYMENT\_SYNC</code>, applied to the Vendor Bill</div>
    </div>
  </div>

  <div style={{ width: '100%', boxSizing: 'border-box', marginTop: '0.35rem', padding: '0.6rem 0.9rem', borderRadius: '10px', border: '1px dashed rgba(148,163,184,0.6)', fontSize: '0.8rem' }}>
    <span style={{ color: '#94a3b8' }}>↩️ Back to Owlery:</span> Ramp's <code>bill.paid</code> webhook marks the original invoice <strong>paid</strong>.
  </div>
</div>

<Info>
  **Who does what.** Owlery creates the Ramp draft bill; a person in Ramp approves and pays it. The **Vendor Bill** and **Bill Payment** in NetSuite are written by **Ramp's own native NetSuite accounting sync** (`BILL_SYNC` / `BILL_PAYMENT_SYNC`) — Owlery never writes to NetSuite directly. The only thing that comes *back* to Owlery is the `bill.paid` webhook, which marks the invoice paid.
</Info>

<Tabs>
  <Tab title="What this means for your accounting team">
    ## What happens, step by step

    <Steps>
      <Step title="Owlery approves the carrier invoice">
        Once a carrier invoice is approved in Owlery (manually or by your auto-decision rules), Owlery sends it to Ramp as a **draft bill**. The bill is pre-filled with the carrier (vendor), invoice number, amount, currency, issue date, and due date. The line items and load numbers travel across as memos so your AP team has context.
      </Step>

      <Step title="Your team reviews and approves the bill in Ramp">
        The draft lands in Ramp as a bill awaiting approval. Nothing is paid yet — this is your control point. Whoever owns AP approves the bill in Ramp exactly as they would any other bill.
      </Step>

      <Step title="Ramp pays the carrier">
        After approval, Ramp processes the payment (ACH, card, check, etc.). The payment details are attached to the bill in Ramp.
      </Step>

      <Step title="NetSuite gets the Vendor Bill and the payment">
        Ramp's built-in NetSuite sync posts the record to NetSuite in **two phases**:

        * **Phase 1 — the Vendor Bill.** When the bill is approved/synced, Ramp creates a matching **Vendor Bill** in NetSuite.
        * **Phase 2 — the payment.** When the bill is paid, Ramp creates a **Bill Payment** in NetSuite and applies it against that Vendor Bill.

        The result in NetSuite looks exactly like a normally entered and paid vendor bill.
      </Step>

      <Step title="Owlery marks the invoice paid">
        When Ramp reports the bill as paid, Owlery updates the original invoice to **paid**, so your freight records and your accounting records stay in agreement — no manual reconciliation.
      </Step>
    </Steps>

    ## What you'll see where

    | Question                      | Where to look                                                       |
    | ----------------------------- | ------------------------------------------------------------------- |
    | Did the bill reach Ramp?      | Ramp → Bills. It appears as a draft/awaiting-approval bill.         |
    | Has it been paid?             | Ramp bill status, and the Owlery invoice flips to **Paid**.         |
    | Is it in the books?           | NetSuite → the Vendor Bill, with a Bill Payment applied against it. |
    | Which carrier / load is this? | The bill memo shows the Owlery invoice and load number(s).          |

    <Note>
      **Approval always lives in Ramp.** Owlery only creates a *draft* — money never moves until a person approves the bill in Ramp. This keeps your existing AP controls and approval chains intact.
    </Note>
  </Tab>

  <Tab title="What this means for your IT team">
    ## System responsibilities

    | Boundary                             | Owner                     | Mechanism                                                  |
    | ------------------------------------ | ------------------------- | ---------------------------------------------------------- |
    | Owlery invoice → Ramp draft bill     | **Owlery**                | Ramp Developer API (`POST /developer/v1/bills/drafts`)     |
    | Draft bill → approved bill           | **Ramp** (human approval) | Ramp app                                                   |
    | Payment execution                    | **Ramp**                  | Ramp bill pay                                              |
    | Ramp bill → NetSuite Vendor Bill     | **Ramp**                  | Ramp native NetSuite accounting sync (`BILL_SYNC`)         |
    | Ramp payment → NetSuite Bill Payment | **Ramp**                  | Ramp native NetSuite accounting sync (`BILL_PAYMENT_SYNC`) |
    | Paid status → Owlery invoice         | **Ramp → Owlery**         | Ramp `bill.paid` webhook                                   |

    <Warning>
      Owlery never calls the NetSuite API in this flow. The Vendor Bill and Bill Payment in NetSuite are written by **Ramp's** accounting integration, which you configure inside Ramp (subsidiary, vendor mapping, GL/expense account mapping, and the `BILL_SYNC` / `BILL_PAYMENT_SYNC` phase settings). If bills aren't appearing in NetSuite, the fix is in Ramp's accounting settings, not Owlery.
    </Warning>

    ## Sequence

    ```mermaid theme={null}
    sequenceDiagram
      autonumber
      participant O as Owlery
      participant R as Ramp API
      participant AP as AP approver
      participant N as NetSuite

      O->>R: POST /developer/v1/bills/drafts<br/>(vendor_id, invoice_number, line_items,<br/>issued_at, due_at, remote_id = invoice.id)
      R-->>O: RampDraftBill { id, status, deep_link_url }
      Note over O: Persist accountingSyncs[] on invoice<br/>{ integrationAuthId, source: Ramp,<br/>sourceId = draft_bill_id, status, syncedAt }

      AP->>R: Approve bill in Ramp
      R-->>N: Phase 1 BILL_SYNC → create Vendor Bill

      AP->>R: Pay bill (ACH / card / check)
      R-->>N: Phase 2 BILL_PAYMENT_SYNC → Bill Payment,<br/>applied against Vendor Bill
      R-->>O: bill.paid webhook (keyed on integrationAuthId + sourceId)
      Note over O: Set accountingSyncs.paidAt →<br/>invoice marked paid
    ```

    ## 1 — Owlery → Ramp (draft bill creation)

    Triggered when an approved invoice is sent to accounting. Owlery resolves the Ramp **vendor** from the invoice's broker via a configured `Broker → Ramp Vendor` mapping, then creates a draft bill.

    * **Endpoint:** `POST https://api.ramp.com/developer/v1/bills/drafts`
    * **Idempotency:** `remote_id` is set to the Owlery invoice ID, so re-sends don't create duplicate bills.
    * **Amounts:** Owlery stores money in cents; the draft-bill payload converts to dollars.
    * **Entity:** `entity_id` is intentionally omitted — the bill routes to the Ramp vendor's default entity.
    * **Payload fields:** `vendor_id`, `invoice_number`, `invoice_currency`, `issued_at`, `due_at`, `remote_id`, `memo` (load numbers/description), `line_items[] { amount, memo }`.

    On success Owlery stores an `accountingSyncs[]` subdocument on the invoice keyed on `(integrationAuthId, sourceId)`, where `sourceId` is Ramp's `draft_bill_id`.

    ## 2 — Ramp → NetSuite (native two-phase sync)

    Configured entirely in **Ramp's** accounting integration for NetSuite:

    * **`BILL_SYNC`** — pushes the approved Ramp bill to NetSuite as a **Vendor Bill**. Requires vendor mapping, subsidiary, and GL/expense account mapping in Ramp.
    * **`BILL_PAYMENT_SYNC`** — pushes the payment to NetSuite as a **Bill Payment / Vendor Payment**, applied against the Vendor Bill from phase 1. Requires a mapped payment/clearing account in Ramp.

    ## 3 — Ramp → Owlery (paid status webhook)

    Ramp fires a `bill.paid` webhook. Owlery matches it to the invoice on `(integrationAuthId, sourceId)` and stamps `accountingSyncs.paidAt`, flipping the Owlery invoice to paid. The write is an **atomic idempotent upsert** — a retried or duplicate webhook can never create a duplicate or drop an existing sync.

    ## Setup checklist

    <AccordionGroup>
      <Accordion title="Ramp Developer app + OAuth">
        Create the Ramp Developer app and connect it in Owlery. Required scopes: `bills:write`, `bills:read`, `vendors:read`, `entities:read`, `accounting:read`. Owlery uses the OAuth2 authorization-code + refresh-token flow (redirect URI `https://owlery.ai/api/auth/integrations/callback/ramp`). See [Connect Ramp to Owlery](/shippers/ramp).
      </Accordion>

      <Accordion title="Broker → Ramp Vendor mapping (in Owlery)">
        On the Ramp integration card in Owlery, configure the **Customized Mapping** from each broker profile to its Ramp vendor. Without it, draft-bill creation fails with a "No Ramp vendor is mapped for this broker" error — Owlery never guesses a vendor.
      </Accordion>

      <Accordion title="Ramp → NetSuite accounting sync (in Ramp)">
        In Ramp's accounting settings, connect NetSuite and enable both the Vendor Bill (`BILL_SYNC`) and Bill Payment (`BILL_PAYMENT_SYNC`) phases. Map subsidiary, vendors, and GL/expense + payment accounts. This is Ramp-side configuration and independent of Owlery.
      </Accordion>

      <Accordion title="bill.paid webhook">
        Ensure Ramp is configured to deliver the `bill.paid` event so Owlery can reconcile paid status back onto the invoice.
      </Accordion>
    </AccordionGroup>

    ## Troubleshooting

    | Symptom                                         | Likely cause                                                                       |
    | ----------------------------------------------- | ---------------------------------------------------------------------------------- |
    | Draft bill never appears in Ramp                | Missing/incorrect Broker → Ramp Vendor mapping, or missing `bills:write` scope.    |
    | Bill in Ramp but not in NetSuite                | Ramp `BILL_SYNC` not enabled, or vendor/subsidiary/GL mapping missing **in Ramp**. |
    | Vendor Bill exists but no payment recorded      | Ramp `BILL_PAYMENT_SYNC` not enabled, or payment account unmapped in Ramp.         |
    | Bill paid in Ramp but Owlery invoice still open | `bill.paid` webhook not delivered/subscribed.                                      |
  </Tab>
</Tabs>
