Resync Entry to Provider
POST/api/ledger/entries/:entry/sync
Push this SINGLE entry to the connected accounting provider SYNCHRONOUSLY (InlineLedgerSyncRunner): the provider round-trip runs inline within the request, so the response already reflects the real Synced / Failed outcome — there is no interim "queued" state to poll. This powers the entry detail page's "Resync Now" button and its live "talking to the provider" animation. The bulk equivalent — POST /api/ledger/entries/bulk with action sync_now — stays an async tracked job.
Any valid API token can call this endpoint — no specific scope required. Manage tokens.
Authentication: Requires Bearer token.
Request body: none.
Locked documents: when the provider document is paid/settled (locked), amounts and dates cannot be pushed. Resync instead pushes only the fields the provider still allows editing on a locked document (the reference/number) and reports amended: true. The entry keeps its "Synced, Locked" status; if the reference change covered all the local drift, the pending-update flag clears. To push amount or date changes, remove the payment or credit note on the provider document first, then Resync. Fully-locked documents: some provider document types (for example a credit note with a payment or allocation applied) are locked so hard that even the reference cannot be pushed. For those, Resync mints no operation and reports locked_blocked: true, and the entry carries a locked_amend_blocked flag; nothing changes on the provider until the payment or allocation is removed there.
Behaviour: mints the entry's manual push op (manual eligibility bypasses the soft per-type Sync toggle but NOT structural blockers — connection inactive, batch member / open batch, internal-only, or before the provider start date) and drives it to the provider in-process. A no-op (still reports synced when already linked-current) when the entry's content hash matches the last sync, or eligible: false when nothing can be pushed.
First-push regeneration: when the entry is imported pre-cutover history that has NEVER been synced (no link on any connection), it is first rebuilt with the current posting rules so the provider's first copy reflects current logic rather than the migrated import. The rebuild may replace the entry with a successor — data.sync.entry_id is the entry the push actually ran against and data.entry is that (possibly new) entry.
Response: 200 with data.sync ({ eligible, synced, failed, amended, locked_blocked, error, regenerated, entry_id }) and data.entry (the refreshed ledger entry object, which includes a locked_amend_blocked flag). error carries the provider rejection message when failed is true. locked_blocked is true when the provider document is fully locked and not even the reference could be pushed. message summarises the outcome.
Permission: requires accounting.sync (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it; 404 when the entry does not exist.
Request
Responses
- 200
- 401
- 403
- 404
- 422
- 429
OK
Response Headers
Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the Errors guide.
Forbidden — the token lacks a required scope, the endpoint is not available to API tokens, or the user behind the token lacks the permission. A human must adjust the token scopes or user permissions; do not retry.
Not found — no record with the given identifier (or the route does not exist). Verify the ID before retrying.
Validation failed — the body is a field → messages map (Laravel shape) or the platform envelope with a stable machine-readable code. Fix the payload and resubmit.
Rate limited — platform limit is 1,000 requests/min; individual tokens may carry lower limits. Honor the Retry-After header before retrying. See the Rate Limits guide.