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

# SFTP data schema and file requirements

> Required objects, file formats, SFTP upload paths, and canonical field schemas for sending data to Daylit over SFTP.

This guide covers the **data** side of an SFTP integration — what to send, where to put it, and how it should be shaped. For SSH keys, connection setup, and portal workflows, see [Connect via SFTP](/settings/sftp).

## The four required objects

Every Daylit SFTP integration must send the four required objects below. They are the minimum needed for open AR, collections workflows, and customer-level reporting. Payment is optional — send it for per-payment-event tracking (see [Payment](#payment)).

| Object               | What it is                                                                              | Example filename     | Required |
| -------------------- | --------------------------------------------------------------------------------------- | -------------------- | -------- |
| **Customer**         | Companies or individuals you bill                                                       | `customers_*.csv`    | ✓        |
| **Customer Contact** | AP, billing, and primary contacts                                                       | `contacts_*.csv`     | ✓        |
| **Invoice**          | Every invoice with embedded payment info (paid amount, paid-on date, balance, due date) | `invoices_*.csv`     | ✓        |
| **Credit Note**      | Refunds, returns, and applied or unapplied credits                                      | `credit_notes_*.csv` | ✓        |
| **Payment**          | Individual payment events — one row per invoice allocation                              | `payments_*.csv`     | Optional |

Daylit accepts additional objects beyond these four without breaking your integration.

## File format

| Setting         | Value                                                                                                                                                              |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Format          | **CSV**                                                                                                                                                            |
| Encoding        | UTF-8 (no BOM)                                                                                                                                                     |
| Delimiter (CSV) | Comma `,`                                                                                                                                                          |
| Quote character | Double quote `"`, escape by doubling: `""` (RFC 4180)                                                                                                              |
| Line endings    | `\n` (LF) or `\r\n` (CRLF)                                                                                                                                         |
| Header row      | **Required** on every file                                                                                                                                         |
| Dates           | `YYYY-MM-DD` (default). Other patterns (`MM-DD-YYYY`, `MM/DD/YYYY`, and more) are selectable per field in the mapping UI.                                          |
| Datetimes       | ISO 8601 UTC — `YYYY-MM-DDTHH:MM:SSZ` (default). `MySQL TIMESTAMP`, `IBM SQL`, and other patterns are selectable per field; timezone-naive values are assumed UTC. |
| Decimals        | Dot separator — `1234.56`. Currency symbols and thousands separators are stripped automatically; accounting-style negatives (`(1234.56)`) become `-1234.56`.       |
| Booleans        | `true`/`false`, `yes`/`no`, or `1`/`0`                                                                                                                             |
| Nulls           | Empty string                                                                                                                                                       |

<h2 id="where-to-put-files-sftp-paths">
  Where to put files (SFTP paths)
</h2>

After provisioning, your SFTP user sees top-level directories for each upload phase. Place files in the matching **object subfolder** within each phase.

### Upload paths

| Path       | Purpose                                               |
| ---------- | ----------------------------------------------------- |
| `/test/`   | Connectivity check — no business validation           |
| `/sample/` | Small file (≤ 100 rows) to validate your mapping      |
| `/init/`   | Full snapshot of every record in an object            |
| `/delta/`  | Incremental updates since your last successful upload |

### Per-object directory structure

```
/test/customer/customers_test_2026-06-04.csv
/test/customer_contact/contacts_test_2026-06-04.csv
/test/invoice/invoices_test_2026-06-04.csv
/test/credit_note/credit_notes_test_2026-06-04.csv
/test/payment/payments_test_2026-06-04.csv

/sample/customer/customers_sample_2026-06-04.csv
/sample/customer_contact/contacts_sample_2026-06-04.csv
/sample/invoice/invoices_sample_2026-06-04.csv
/sample/credit_note/credit_notes_sample_2026-06-04.csv
/sample/payment/payments_sample_2026-06-04.csv

/init/customer/customers_init_2026-06-04.csv
/init/customer_contact/contacts_init_2026-06-04.csv
/init/invoice/invoices_init_2026-06-04.csv
/init/credit_note/credit_notes_init_2026-06-04.csv
/init/payment/payments_init_2026-06-04.csv

/delta/customer/customers_delta_2026-06-04T140000Z.csv
/delta/customer_contact/contacts_delta_2026-06-04T140000Z.csv
/delta/invoice/invoices_delta_2026-06-04T140000Z.csv
/delta/credit_note/credit_notes_delta_2026-06-04T140000Z.csv
/delta/payment/payments_delta_2026-06-04T140000Z.csv
```

**Rules:**

* If a daily delta has nothing to send for an object, skip that object. Daylit won't penalize an empty interval. Daylit warns if a normally active object goes silent for more than 1.5× its usual cadence.
* Send one file per object per push. For large inits, use a single `.csv.gz` rather than splitting into parts.

### Upload order for init

Push parents before children so foreign keys resolve immediately:

1. `customer` first
2. `customer_contact` (references Customer)
3. `invoice` (references Customer)
4. `credit_note` (references Customer)
5. `payment` (optional; references Invoice)

Daylit can accept files in any order and holds ingestion until referenced parents exist, but dependency order surfaces FK errors sooner.

## Canonical schema per object

Every object has a **canonical schema** — the field names and types Daylit expects after your mapping is applied. You don't rename columns in your source system; you map them in the portal on **Settings ->** [SFTP connection](https://intelligence.daylit.com/settings/sftp).

Schemas are versioned. Schemas below are v1.

### Conventions used in every object

| Field               | Purpose                                                                                                             |
| ------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `remote_id`         | The record's identifier in your source system. **Required on every row.** Daylit upserts on this value.             |
| `remote_updated_at` | ISO 8601 UTC timestamp of the last modification in your source system. Must increase monotonically per `remote_id`. |
| `*_remote_id`       | Foreign key to another object's `remote_id` (for example, `customer_remote_id` on an invoice).                      |
| `status`            | Normalized lifecycle state. Allowed values are listed per object.                                                   |

### Customer

One row per customer. Daylit upserts on `remote_id` — a repeated `remote_id` updates the existing customer, a new one inserts. Upload to `/init/customer/` and `/delta/customer/`. Customer is the parent record, so make sure to upload Customers before any other object.

| Field                   | Type     | Required | Notes                                                                                                   |
| ----------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------- |
| `remote_id`             | string   | ✓        | Unique per customer in your source system                                                               |
| `customer_company_name` | string   | ✓        | Legal/billing entity name                                                                               |
| `remote_updated_at`     | datetime | ✓        | Source-system last-modified timestamp. Drives incremental sync.                                         |
| `address_line1`         | string   | optional |                                                                                                         |
| `address_line2`         | string   | optional |                                                                                                         |
| `address_city`          | string   | optional |                                                                                                         |
| `address_state`         | string   | optional | 2-letter US state or ISO 3166-2 subdivision                                                             |
| `address_zipcode`       | string   | optional |                                                                                                         |
| `address_country`       | string   | optional | ISO 3166-1 alpha-2 (`US`, `GB`, `IN`). Can be set to a static value if all customers share one country. |
| `default_payment_terms` | string   | optional | Net-N days, e.g. `30` for Net 30                                                                        |
| `credit_limit`          | decimal  | optional |                                                                                                         |

### Customer Contact

One row per contact; a customer can have zero, one, or many. Daylit upserts on `remote_id`. Upload to `/init/customer_contact/` and `/delta/customer_contact/`, after the customers they reference via `customer_remote_id`.

| Field                | Type    | Required | Notes                                                               |
| -------------------- | ------- | -------- | ------------------------------------------------------------------- |
| `remote_id`          | string  | ✓        | Unique per contact                                                  |
| `customer_remote_id` | string  | ✓        | FK to `Customer.remote_id`                                          |
| `email`              | string  | ✓        | Primary email for this contact                                      |
| `first_name`         | string  | optional |                                                                     |
| `last_name`          | string  | optional |                                                                     |
| `phone`              | string  | optional | E.164 preferred                                                     |
| `title`              | string  | optional | Job title, e.g. Accounts Payable Manager                            |
| `department`         | string  | optional | e.g. Finance, Accounting                                            |
| `is_primary`         | boolean | optional | `true` if this is the customer's main contact. Defaults to `false`. |

<Tip>
  If your source stores contacts as columns on the customer record (`billing_email`, `billing_contact_name`), you can emit a synthetic Contact row per customer using `customer_remote_id + "_primary"` as the contact's `remote_id`, or put the email on the Customer record and skip Contacts. Contact [Daylit](/settings/sftp#getting-help) if you're unsure which fits your data shape.
</Tip>

### Invoice

One row per invoice. Payment summary fields (`paid_amount`, `paid_on_date`, `balance`) are **embedded on the invoice row**, not sent as a separate object. Daylit upserts on `remote_id`. Upload to `/init/invoice/` and `/delta/invoice/`, after the customers they reference via `customer_remote_id`.

| Field                | Type                    | Required | Notes                                                     |
| -------------------- | ----------------------- | -------- | --------------------------------------------------------- |
| `remote_id`          | string                  | ✓        | Unique per invoice                                        |
| `customer_remote_id` | string                  | ✓        | FK to `Customer.remote_id`                                |
| `remote_updated_at`  | datetime (ISO 8601 UTC) | ✓        |                                                           |
| `number`             | string                  | ✓        | Human-readable invoice number                             |
| `issue_date`         | date                    | ✓        | Date the invoice was issued                               |
| `due_date`           | date                    | ✓        | Date the invoice is due                                   |
| `total_amount`       | decimal                 | ✓        | Grand total, including taxes and fees                     |
| `paid_amount`        | decimal                 | ✓        | Cumulative amount paid. `0` if unpaid                     |
| `balance`            | decimal                 | ✓        | Send explicitly — don't make Daylit calculate it          |
| `paid_on_date`       | date                    | optional | Date the invoice was fully paid. Empty if not fully paid. |
| `currency`           | string                  | ✓        | ISO 4217                                                  |
| `status`             | enum                    | ✓        | See allowed values below                                  |
| `memo`               | string                  | optional |                                                           |

**Allowed `status` values:**

| Value            | Meaning                                           |
| ---------------- | ------------------------------------------------- |
| `DRAFT`          | Not finalized or sent; excluded from AR totals    |
| `SUBMITTED`      | Issued to the customer; payment still expected    |
| `OPEN`           | Outstanding, amount still due                     |
| `PARTIALLY_PAID` | Some payment received, balance > 0                |
| `PAID`           | Fully paid, balance = 0                           |
| `VOID`           | Cancelled or invalidated; excluded from AR totals |

<Tip>
  If your source system uses different status text (for example `Fully Paid` or `Not Sent`), map it to a canonical value in the portal mapping UI under the `status` field. Matching is case-insensitive.
</Tip>

### Credit Note

One row per credit note — refunds, returns, and credit memos issued to customers. Daylit upserts on `remote_id`. Upload to `/init/credit_note/` and `/delta/credit_note/`, after the customers they reference via `customer_remote_id`.

| Field                | Type     | Required | Notes                                                       |
| -------------------- | -------- | -------- | ----------------------------------------------------------- |
| `remote_id`          | string   | ✓        | Unique per credit note                                      |
| `customer_remote_id` | string   | ✓        | FK to `Customer.remote_id`                                  |
| `total_amount`       | decimal  | ✓        | Always **positive** — Daylit treats it as a credit          |
| `remote_updated_at`  | datetime | optional | Source-system last-modified timestamp                       |
| `number`             | string   | optional | Human-readable credit note number                           |
| `transaction_date`   | date     | optional | Date the credit note was issued                             |
| `status`             | enum     | optional | See below. Defaults to `SUBMITTED`.                         |
| `remaining_credit`   | decimal  | optional | Unapplied credit still available. `0` if fully applied.     |
| `currency`           | string   | optional | ISO 4217. Defaults to `USD`.                                |
| `exchange_rate`      | decimal  | optional | Rate at issuance, if multicurrency                          |
| `inclusive_of_tax`   | boolean  | optional | `true` if `total_amount` includes tax. Defaults to `false`. |
| `memo`               | string   | optional |                                                             |

**Allowed `status` values:**

| Value        | Meaning                                          |
| ------------ | ------------------------------------------------ |
| `SUBMITTED`  | Issued but not yet approved or applied (default) |
| `AUTHORIZED` | Approved and available to apply against invoices |
| `PAID`       | Fully applied; no remaining credit               |

<Tip>
  If your source system uses different status text (for example `Open` or `Applied`), map it to a canonical value in the portal mapping UI under the `status` field.
</Tip>

<h3 id="payment">
  Payment
</h3>

Daylit ingests payments as **one row per invoice allocation**. A single payment applied across several invoices is sent as multiple rows that share the same `remote_id`, each pointing at a different `invoice_remote_id` with its own `applied_amount`. Upload to `/init/payment/` and `/delta/payment/`, after the invoices they reference.

| Field               | Type     | Required | Notes                                                                                                                            |
| ------------------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `remote_id`         | string   | ✓        | Payment identifier. Repeated across rows when one payment covers multiple invoices.                                              |
| `invoice_remote_id` | string   | ✓        | FK to `Invoice.remote_id` — the invoice this row applies to                                                                      |
| `total_amount`      | decimal  | ✓        | Total amount of the payment (the same value on every row for that payment)                                                       |
| `applied_amount`    | decimal  | optional | Portion of the payment applied to this invoice row. If omitted, the total amount is assumed to be applied to the linked invoice. |
| `transaction_date`  | date     | optional | Date the payment was received                                                                                                    |
| `currency`          | string   | optional | ISO 4217. Defaults to `USD`.                                                                                                     |
| `status`            | string   | optional | Source payment status, e.g. `completed`, `pending`                                                                               |
| `payment_method`    | string   | optional | e.g. `ach`, `card`, `wire`, `check`                                                                                              |
| `transaction_id`    | string   | optional | Bank or processor reference                                                                                                      |
| `notes`             | string   | optional | Free-form note                                                                                                                   |
| `remote_updated_at` | datetime | optional | Source-system last-modified timestamp                                                                                            |

## Sample CSV files

Example exports showing what each object looks like as a CSV — typical column names from a source system, not the canonical Daylit field names. Use them to understand the shape of each table, as a starting point for your own exports, or to map columns in **Settings →** [SFTP connection](https://intelligence.daylit.com/settings/sftp). See the [canonical schema](#canonical-schema-per-object) above for the fields Daylit expects after mapping.

For full sample data across all five objects, open the [Sample CSV spreadsheet](https://docs.google.com/spreadsheets/d/1QfaRYnyznQ_-PlrRHEnpLrKjNuO2nR1qMDBhs2dXtfY/edit?usp=sharing) — one tab per object. Use **File → Download** in Google Sheets to export as CSV.

<Tip>
  Customer, contact, invoice, credit note, and payment rows share the same IDs across these files (for example, `CUST-900001` and `INV-900001`). Match that pattern in your exports so foreign keys line up between objects.
</Tip>

<AccordionGroup>
  <Accordion title="Customer — customers_sample.csv">
    ```csv customers_sample.csv theme={null}
    ERP_Customer_ID,Customer_Name,Last_Modified,Billing_Address1,Billing_Address2,Billing_City,Billing_State,Billing_Zip,default_payment_terms
    CUST-900001,Sterling Enterprises 620,09-07-2024 3:01:47,4507 Cedar Dr,Suite 9035,Chicago,IL,60601,
    CUST-900002,Quantum Enterprises 3711,05-24-2026 2:13:14,8280 Hill St,,Portland,OR,97201,NET 60
    CUST-900003,Vertex Group 3627,03-21-2025 18:17:51,107 Sunset Blvd,,Austin,TX,78701,
    ```
  </Accordion>

  <Accordion title="Customer Contact — contacts_sample.csv">
    ```csv contacts_sample.csv theme={null}
    ERP_Contact_ID,ERP_Customer_ID,First_Name,Last_Name,Email,Phone,Title,Is_Primary
    CONT-900001,CUST-900001,Tina,Nguyen,tina.nguyen0@sterlingenterprises620.com,(424) 851-4115,Billing Manager,TRUE
    CONT-900002,CUST-900002,Elena,Davis,elena.davis0@quantumenterprises3711.com,(842) 861-1645,AP Manager,TRUE
    CONT-900003,CUST-900003,Elena,Chen,elena.chen0@vertexgroup3627.com,(502) 534-7807,Accounts Payable Specialist,TRUE
    ```
  </Accordion>

  <Accordion title="Invoice — invoices_sample.csv">
    ```csv invoices_sample.csv theme={null}
    ERP_Invoice_Number,Customer_Account_ID,Updated_At,Invoice_Number,Date_Billed,Due_Date,Paid_On,Grand_Total_USD,Paid_Total_USD,Open_Balance_USD,Merge_Status
    INV-900001,CUST-900001,10-03-2024 17:05:32,IN-900001,08-23-2024,10-07-2024,11-06-2024,1730.31,1730.31,0,PAID
    INV-900002,CUST-900002,01-07-2026 19:46:57,IN-900002,12-19-2025,01-18-2026,03-02-2026,1636.33,1636.33,0,PAID
    INV-900003,CUST-900003,06-14-2026 1:41:49,IN-900003,04-13-2026,04-28-2026,05-10-2026,1561.97,1561.97,0,PAID
    ```
  </Accordion>

  <Accordion title="Credit Note — credit_notes_sample.csv">
    ```csv credit_notes_sample.csv theme={null}
    ERP_Credit_Note_ID,ERP_Customer_ID,Credit_Note_Number,Transaction_Date,Status,Total_Amount,Remaining_Credit,Exchange_Rate,Inclusive_Of_Tax,Memo,Updated_At
    CN-900001,CUST-900001,CR-900001,06-27-2025,SUBMITTED,518.29,518.29,1,FALSE,Promotional credit,12-09-2025 0:18:26
    CN-900002,CUST-900002,CR-900002,09-19-2025,SUBMITTED,232.16,232.16,1,FALSE,Service credit,10-01-2025 5:43:24
    CN-900003,CUST-900003,CR-900003,01-06-2025,AUTHORIZED,371.7,50.41,1,FALSE,Returned goods credit,01-26-2026 2:10:27
    ```
  </Accordion>

  <Accordion title="Payment — payments_sample.csv">
    ```csv payments_sample.csv theme={null}
    ERP_Payment_ID,Payment_Total_USD,Updated_At,Invoice_ID,Applied_Amount,Payment_Date,Payment_Status,Payment_Method,Payment_Reference,Notes
    PAY-900001,1730.31,12-26-2025 7:37:20,INV-900001,1730.31,11-06-2024,processing,Wire,REF-PAY-900001,Batch settlement
    PAY-900002,1636.33,06-21-2026 22:44:22,INV-900002,1636.33,03-02-2026,processing,Wire Transfer,REF-PAY-900002,Customer payment
    PAY-900003,1561.97,05-19-2026 6:53:51,INV-900003,1561.97,05-10-2026,pending,Wire Transfer,REF-PAY-900003,Partial payment
    ```
  </Accordion>
</AccordionGroup>

## Foreign keys and object relationships

```mermaid actions={false} theme={null}
%%{init: {"theme":"base","themeVariables":{
  "fontFamily":"Plus Jakarta Sans, -apple-system, sans-serif",
  "background":"#fefdf8","primaryColor":"#ffffff","primaryTextColor":"#3d0c11",
  "primaryBorderColor":"#e8e5df","lineColor":"#3d0c11",
  "secondaryColor":"#fef9c3","tertiaryColor":"#fefdf8"
}}}%%
flowchart TD
    C(["Customer"])

    C -->|"customer_remote_id"| CC["Customer Contact"]
    C -->|"customer_remote_id"| I["Invoice"]
    C -->|"customer_remote_id"| CN["Credit Note"]
    I -->|"invoice_remote_id"| P["Payment"]
```

**Rules:**

* `CustomerContact.customer_remote_id` must match an existing `Customer.remote_id`.
* `Invoice.customer_remote_id` must match an existing `Customer.remote_id`.
* `CreditNote.customer_remote_id` must match an existing `Customer.remote_id`.
* `Payment.invoice_remote_id` must match an existing `Invoice.remote_id`.
* Foreign keys resolve at the end of each batch. If a child file arrives before its parent, Daylit holds it for up to 24 hours, then quarantines it with an FK-unresolved error.

## Validation rules summary

A file passes validation when **all** of the following hold:

**Structure**

* Path is `/{phase}/{object}/{filename}` with recognized phase and object values.
* Filename matches `{object}_{phase}_{date}`.
* File parses cleanly: correct encoding, header row present, no truncated rows.

**Per row**

* Every required field is non-empty.
* Every cell satisfies its declared type.
* `remote_id` is non-empty.

**Per file**

* `remote_id` is unique within the file.
* For `init`: row count is between 1 and 10× the largest sample ever sent for this object.
* For `delta`: `remote_updated_at` per `remote_id` is monotonically increasing.

**Cross-file (init/delta)**

* Every `*_remote_id` foreign key resolves to a known parent record.

If validation fails, the file is quarantined with a failure report listing the first 10 failing rows and the reason for each. Fix and re-upload — no penalty.

## Worked example: ACME Corporation's first init

ACME Corporation exports all five objects and uploads in order:

```bash theme={null}
sftp daylit
sftp> put customers_init_2026-06-04.csv      /init/customer/
sftp> put contacts_init_2026-06-04.csv       /init/customer_contact/
sftp> put invoices_init_2026-06-04.csv       /init/invoice/
sftp> put credit_notes_init_2026-06-04.csv   /init/credit_note/
sftp> put payments_init_2026-06-04.csv       /init/payment/
sftp> bye
```

Within a few minutes the portal shows:

```
✓ Customer         init    1,842 rows ingested
✓ Customer Contact init    2,114 rows ingested
✓ Invoice          init   18,422 rows ingested
✓ Credit Note      init      211 rows ingested
✓ Payment          init    4,037 rows ingested
```

From this point, ACME's cron uploads hourly deltas to `/delta/{object}/` and Daylit stays current.

## Need help?

* **Portal in-app chat** — bottom-right on every page.
* **Email:** [support@daylit.com](mailto:support@daylit.com). Include your workspace name and the filename you're asking about.
* **Status page:** [status.daylit.com](https://status.daylit.com) for any active incidents.
