Skip to main content
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.

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). Daylit accepts additional objects beyond these four without breaking your integration.

How to use this schema

1

Start with the required objects

Prepare Customer, Customer Contact, Invoice, and Credit Note files first. Add Payment only if you want separate per-payment-event tracking.
2

Match each source export to an object

Use the object sections below to identify which source columns map to Daylit’s canonical fields. Keep your source column names as-is; you will map them in Daylit.
3

Map columns in Daylit

In Settings → SFTP Connection, upload a sample file for each object and map your source columns to the canonical fields in this guide.
4

Use sample CSVs as a reference

Compare your exports with the sample CSV files to confirm file shape, IDs, dates, and foreign keys.
5

Validate sample files

Upload sample files to /sample/{object}/ and confirm validation passes before sending a full /init/ snapshot.

File format

Where to put files (SFTP paths)

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

Per-object directory structure

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 under Settings → SFTP Connection. Schemas are versioned. Schemas below are v1.

Conventions used in every 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.

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.
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 if you’re unsure which fits your data shape.

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. Allowed status values:
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.

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. Allowed status values:
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.

Payment

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.

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 under Settings → SFTP Connection. See the canonical schema above for the fields Daylit expects after mapping. For full sample data across all five objects, open the Sample CSV spreadsheet — one tab per object. Use File → Download in Google Sheets to export as CSV.
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.
customers_sample.csv
contacts_sample.csv
invoices_sample.csv
credit_notes_sample.csv
payments_sample.csv

Foreign keys and object relationships

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:
Within a few minutes the portal shows:
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. Include your workspace name and the filename you’re asking about.
  • Status page: status.daylit.com for any active incidents.