Contractors

contractorOps.import.contractors

Bring contractors in from a table: source notion with databaseId (the database's link or ID), or source csv with rows. mapping names the column for any fields whose column names don't match (null for none); the others are matched by name, and a column that isn't in the table is reported in problems. A required field no column matches is refused with the table's columns listed. Fields: name (required), legalName, workEmail, personalEmail, wiseEmail, wiseLink, phone, roles, jobRole, department, jurisdiction, address (split into street, city, province or state, postal code and country), city, postalCode, chargesSalesTax, salesTaxRate, discord, github, portfolio, startDate, notes, currency, and the bank columns accountHolderName, bankName, bankAddress, accountNumber, institutionNumber, transitNumber, routingNumber, iban and swiftBic. People already in Oatmilk are matched by email and then by name, and only have blanks filled in; new people are added without an invitation, and no one is emailed. jobRole is matched to a job role by title and level; a title with several levels takes the level whose pay band holds the person's current rate, and otherwise jobRoleLevels lists the levels to choose from. The address used to reach each person is work email, then personal, then Wise, unless emails (row reference to address) chooses another; a row with no email is left out until emails gives it one. Payment details are saved only when an administrator imports and only for someone with none yet, in the currency column's currency, else the bank's country's, else the person's country's, else CAD. They are validated, sealed and audited, and saving them doesn't email the contractor; a row whose details don't validate is still imported and listed in paymentProblems. Payment details are never returned, in a preview, a result or a message: each row's plan has paymentDetails with present, method (bank_transfer or wise_email), masked (at most the last four characters of the account number or IBAN, or the Wise email masked) and fields (the names of the details the row has), a Wise email is left out of emails and masked when it's the address used, and a column whose name says it holds payment or tax details is only read for the bank and Wise fields. With preview true nothing is saved and the plan for every row comes back.

POST/api/v1/accounting/contractorOps.import.contractors

Permissions

accounting:readaccounting:write

Who can call it

admin, finance

Retries

Idempotency key required

MCP

accounting_contractor_ops_import_contractors

Fields

  • sourceenumRequired

    notion reads a Notion database on the server; csv takes the rows of a parsed CSV file.

    csvnotion
  • rowsarray of maps

    For csv: each row as column name to value, at most 500.

    1–500 items

  • databaseIdstring

    For notion: the database's link or ID. The database must be shared with the Oatmilk connection.

    1–2000 characters

  • mappingobject

    The column for any fields whose column names don't match (null for none). Fields left out, or every field when it's omitted, are matched by column name; the result returns the mapping it used, so a preview shows the suggested one.

  • previewboolean

    true checks every row and saves nothing.

  • idempotencyKeystringRequired

    Any unique text you generate once per intended change, so a retried request only happens once. Send it as the Idempotency-Key header instead if you prefer; if you send both they must match.

    8–200 characters

  • emailsmap

    The address to use for a row, by the row's reference (its Notion page link, or Row 2, Row 3 for a CSV file), instead of work, personal, then Wise email.

Example

curl https://app.getoatmilk.com/api/v1/accounting/contractorOps.import.contractors \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "source": "csv",
  "rows": [
    {}
  ]
}'
Response
{
  "data": { … }
}

Try it

Try it

Checks your input with this action’s real schema and answers like the API, with synthetic data. No key needed, and nothing changes.

POST/api/v1/accounting/contractorOps.import.contractors
curl https://app.getoatmilk.com/api/v1/accounting/contractorOps.import.contractors \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "source": "csv",
  "rows": [
    {}
  ]
}'

More in Contractors.