Skip to content

Write commands silently drop nested fields; --file payload largely ignored on contacts and quotes (v0.0.3) #12

@cameronstewart

Description

@cameronstewart

Summary

Running xero@0.0.3 against a live AU org I hit several cases where the CLI accepts a write command, returns success, but silently drops fields that the Xero API supports. Most painfully, --file is essentially ignored on contacts create/update and quotes create — only flag-supplied scalar fields make it through. There are no warnings or validation errors.

Environment

  • Package: @xeroapi/xero-command-line v0.0.3
  • Platform: Linux (WSL2 Ubuntu)
  • Tenant: AU organisation, AUD, single Xero profile (PKCE OAuth flow worked fine)

Reproducer 1 — contacts create --file silently drops everything except name

cat > contact.json <<'JSON'
{
  "name": "Acme Pty Ltd",
  "firstName": "Jane",
  "lastName": "Doe",
  "emailAddress": "jane@acme.example",
  "phones": [
    { "phoneType": "DEFAULT", "phoneNumber": "1234 5678", "phoneAreaCode": "02", "phoneCountryCode": "61" }
  ],
  "addresses": [
    { "addressType": "STREET", "addressLine1": "1 Test St", "city": "Sydney", "region": "NSW", "postalCode": "2000", "country": "Australia" }
  ],
  "contactPersons": [
    { "firstName": "Jane", "lastName": "Doe", "emailAddress": "jane@acme.example", "includeInEmails": true }
  ],
  "isCustomer": true
}
JSON

xero contacts create --file contact.json --json
# => Error: Validation errors: name: Required
#    (--name flag is required even when --file contains "name")

xero contacts create --name "Acme Pty Ltd" --file contact.json --json
# => 201 success, but the resulting contact has:
#    - name: "Acme Pty Ltd"               OK
#    - firstName/lastName/emailAddress    NOT PERSISTED
#    - phones[].phoneNumber               NOT PERSISTED
#    - addresses[].addressLine1/city/...  NOT PERSISTED
#    - contactPersons                     NOT PERSISTED (empty [])
#    - isCustomer                         NOT PERSISTED (false)

Tested key casings: lowercase camelCase (per Xero API spec) and PascalCase — both behave identically; neither persists nested fields.

Reproducer 2 — contacts update --phone is silently ignored

xero contacts update \
  --contact-id <uuid> \
  --name "Acme Pty Ltd" \
  --phone "+61 2 1234 5678" \
  --json
# => 200 success, response echoes the call but phones[].phoneNumber remains "" on read-back

Confirmed by reading the contact back via xero contacts list --search "Acme" — the phone never persists.

Reproducer 3 — quotes create --file silently ignored entirely

cat > quote.json <<'JSON'
{
  "contact": {"contactID": "<uuid>"},
  "date": "2026-05-14",
  "lineAmountTypes": "EXCLUSIVE",
  "title": "Test Quote",
  "summary": "Summary text",
  "terms": "Terms text",
  "lineItems": [
    { "itemCode": "ITEM-A", "description": "Day 1", "quantity": 1, "unitAmount": 1100, "accountCode": "200", "taxType": "OUTPUT" },
    { "itemCode": "ITEM-B", "description": "Project",  "quantity": 1, "unitAmount": 1000, "accountCode": "200", "taxType": "OUTPUT" }
  ]
}
JSON

xero quotes create --file quote.json --json
# => Error: Validation errors: contactId: Required
#    (file contents not used for the contactId pre-validation)

xero quotes create --contact-id <uuid> --file quote.json --json
# => Error: Validation errors: contactId: Required (same — --file still not consulted)

xero quotes create --contact-id <uuid> --date 2026-05-14 --file quote.json --json
# => Error: Validation errors: contactId: Required

I was unable to find any flag combination that lets --file provide line items. The only way to create a quote via this CLI is single-line via flags. Combined with xero quotes update having no line-item flags at all, multi-line quotes appear to be impossible via the CLI.

Reproducer 4 — contacts update --file requires --contact-id flag even when file contains contactID

xero contacts update --file update.json --json
# => Error: Validation errors: contactId: Required
# even though update.json contains: { "contactID": "...", "name": "...", ... }

Expected behaviour

  • --file should be the canonical way to provide structured payloads (especially line items, multi-address contacts, etc.). The Xero REST API supports all these fields — the CLI is dropping them client-side.
  • If a flag must duplicate a field that's already in --file, the CLI should either (a) read it from the file as a fallback, or (b) document the constraint clearly in --help.
  • Silent drops should be hard errors, not no-ops.

Suggested fixes (in priority order)

  1. Honour --file payloads end-to-end on contacts create/update and quotes create — including nested phones[], addresses[], contactPersons[], lineItems[].
  2. Surface dropped fields as validation errors rather than silently ignoring them.
  3. Remove the --name/--contact-id flag-must-be-present-even-with-file requirement, or make the file fields satisfy the pre-validation.
  4. Add line-item flags to quotes update (--line-item-description, etc.) for parity with quotes create, or accept multi-line via --file on update.
  5. Document the current limitations in the README until the above is fixed — particularly the "single-line only" reality for quotes.

Impact

For typical small-business use (an AUD org, weekly proposal quotes, occasional new contacts), the CLI is currently only usable for reads. Every quote with multiple line items and every new client contact with a phone or address has to be done in the Xero UI instead.

Happy to test patches if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions