Skip to content

Latest commit

 

History

History
78 lines (62 loc) · 3.82 KB

File metadata and controls

78 lines (62 loc) · 3.82 KB

NeetoInvoice integration validation

Validated on July 29, 2026 against NeetoInvoice’s documented MCP server at https://connect.neetoinvoice.com/mcp/messages.

Supported operations used by the app

  • Bearer authentication with a NeetoInvoice API key
  • ListTeamMembers
  • ListClients
  • ListProjects, filtered with the selected team member’s email
  • GetProject, including the project’s client and tasks
  • CreateProject, including one or more initial tasks
  • ListTimeEntries, filtered to today for each available project
  • CreateTimeEntry
  • DeleteTimeEntry, taking the entry ID as time_entry_id — confirmed against a live workspace on July 30, 2026. This argument name is not published anywhere: NeetoInvoice's public REST API at apidocs.neetoinvoice.com exposes only GET and POST /time-entries and has no delete operation at all, so the MCP server offers this beyond the documented REST surface. Record any future tool's argument names here; a tool name alone is not enough to call it.

The app does not call private browser endpoints.

Confirmed limitations

CreateTimeEntry does not advertise idempotency and does not accept an idempotency key. Its MCP metadata explicitly reports idempotentHint: false. No documented server-enforced unique client identifier was found.

For that reason the app deliberately does not blindly retry an upload whose result is uncertain. A connection known to be offline remains ready for a manual retry. A timeout, interrupted response, or other ambiguous result is marked Needs Verification, and retry is disabled until the user checks NeetoInvoice. This is the only safe behavior available without a documented idempotency guarantee.

hours is accepted as a JSON number. NeetoInvoice does not document a maximum decimal scale in the MCP schema. The app retains integer milliseconds locally, calculates hours with Swift Decimal, and converts only at the MCP JSON boundary. It never applies project rounding locally.

Remaining production gate

Do not describe v0.1 as providing automatic exactly-once delivery. That acceptance criterion requires NeetoInvoice to add either:

  1. a documented idempotency key for CreateTimeEntry, or
  2. a documented client-generated unique ID with uniqueness enforced by the server.

Until then, the conservative Needs Verification state prevents an uncertain upload from being repeated by the app.

CreateProject is also marked idempotentHint: false. The app never retries an uncertain project-creation response and asks the user to refresh and verify the NeetoInvoice catalog first.

ListTimeEntries returns unbilled entries for one project at a time. The Today view requests every page for each active project available to the selected user, merges those server entries with local pending entries, and removes duplicates by server entry ID. Already-billed entries are not exposed by this documented history operation. NeetoInvoice's public REST documentation states the same constraint: "Only time entries not associated with billed invoices are returned."

That constraint is what bounds automatic verification. The app reconciles an entry left in Needs Verification by searching this history for a match on project, date, user, task, note, and duration. Because a billed entry is absent from the results, a missing match proves nothing — verification may therefore only ever confirm that an upload landed, never conclude that it did not. An unmatched or ambiguous entry stays in Needs Verification for the user.

There is no update operation for a time entry. Changing one that already reached NeetoInvoice would mean deleting and recreating it, which gives the entry a new ID and drops anything set outside this app, so the app does not do it: it edits only entries that never reached NeetoInvoice, and sends the user to the web app for the rest.