Sync Status Updates
The shared close-the-loop mechanism used by every accounting record type. Cross-references your target system's ID onto the Tai record, keeps the batch queue clean, and powers the failure retry loop through Accounting Sync History.
What this is for
Sync status is how you tell Tai "I got this record, and here's what happened." Every record that leaves Tai via Outbound Data Transmission, and every record you create in Tai via Inbound Data Transmissions, carries a sync status. If you skip this call:
- Outbound: the record stays at
SyncStatus=Nonein Tai, and every batch pull will keep serving it to you forever. There's no other way for Tai to know you handled it. - Inbound: the record you just created lives in Tai without a cross-reference to your target system. When someone in six months tries to reconcile a discrepancy, they'll have no way to find the corresponding record in your ERP.
Sync status is small, cheap to call, and non-negotiable. Every attempt (success or failure) gets a sync call.
The shared shape
Every sync endpoint uses the same 3-field request payload:
statusβ the outcome (see enum below).transactionIdβ the target system's primary key for the record (on success), or a datetime stamp (on failure).- The record's Tai ID β
billId,invoiceId,billPaymentId, orinvoicePaymentIddepending on which endpoint you're hitting.
The response is a plain string (not a JSON object), e.g. "Invoice 14041027 successfully updated."
The ExternalSyncStatus enum
ExternalSyncStatus enum| Value | When to use it |
|---|---|
None | The initial state on every new record in Tai. You generally don't send this β Tai sets it. |
Complete | You successfully pushed the record to the target system and got a positive acknowledgement. transactionId = target system's primary key. |
Failed | You attempted the push and the target system rejected it (validation error, missing reference, whatever). transactionId = a datetime stamp (e.g. 20260904) so the attempt is at least recorded. |
AlreadySynced | Idempotency case β you re-processed a record that had already been pushed. Rare but useful when your integration is replaying a queue. |
Updated | The record was updated on the target side after an initial sync. Used when you've re-pushed a revision. |
How sync status appears on the record
Once you've called sync, the Tai record's externalSync block reflects the outcome. When you re-read the record (via GET /Bills/{billId}, for example), you'll see:
"externalSync": {
"date": "2026-09-04T20:00:51.397+00:00",
"status": "Complete",
"referenceNumber": "SAP-DOC-4400218994"
}dateβ when Tai recorded the sync outcome.statusβ the last value you sent.referenceNumberβ thetransactionIdyou sent (Tai stores it underreferenceNumberon the record).
The four sync endpoints
All four share the same shape β only the ID field name changes.
Bills
PUT /PublicApi/Accounting/v2/Bills/Sync
Request payload
{
"billId": 18042026,
"status": "Complete",
"transactionId": "SAP-DOC-4400218994"
}Response (200)
"Bill 18042026 successfully updated."
Invoices
PUT /PublicApi/Accounting/v2/Invoices/Sync
Request payload
{
"invoiceId": 14041027,
"status": "Complete",
"transactionId": "SAP-INV-9900341882"
}Response (200)
"Invoice 14041027 successfully updated."
Bill Payments
PUT /PublicApi/Accounting/v2/BillPayments/Sync
Request payload
{
"billPaymentId": 1687709,
"status": "Complete",
"transactionId": "SAP-PAY-77118203"
}Invoice Payments
PUT /PublicApi/Accounting/v2/InvoicePayments/Sync
Request payload
{
"invoicePaymentId": 2021562,
"status": "Complete",
"transactionId": "SAP-RCPT-3320019844"
}Failure payload
Any of the four endpoints, when the target-system push failed:
{
"billId": 18042026,
"status": "Failed",
"transactionId": "20260904"
}transactionId is required even on failure β a datetime stamp is a fine placeholder. What matters is that the record moves out of SyncStatus=None so the batch queue is clean and Tai's Accounting Sync History has an entry to display.
The retry loop
A status=Failed sync doesn't lose the record β it hands it to a Tai user to fix. Here's the loop:
- You push to the target, it rejects. Say the target's error was "Bill To Address line 1 is required."
- You call
PUT /Syncwithstatus=Failed. The Tai record now shows as failed sync. - You call
POST /ShipmentActivityLogson the same shipment with the error text so a Tai user sees it inline. (See Shipment Activity Logs for how to write this well.) - A Tai user opens the Accounting Sync History page, sees the failed record with your error message, fixes the underlying data (adds the missing address, corrects the wrong customer, whatever).
- The user moves the record from
FailedβPending. Tai flips the sync status back toNone. - The record shows up on your next batch pull. You re-attempt the push. If it succeeds this time,
status=Completeand the loop closes.
Without steps 2 and 3, the loop breaks β the record either sits at None forever (silently re-served on every batch, cluttering your queue) or the Tai user has no idea why it failed and no way to fix it.
Idempotency
PUT /Sync is idempotent β safe to call multiple times with the same payload. Useful for retry logic in your integration: if the sync-status call itself fails (network hiccup), just try again. Tai reconciles to the last call.
The only value that changes state each time is status β if you first send Failed and then later send Complete after a successful retry, Tai reflects the current outcome.
Where this is called from
- Every outbound flow: AR Records, AP Records, AR Payments, AP Payments.
- Every inbound create/approve: AP Records, AR Records, AP Payments, AR Payments.
Pair every sync call with a matching Shipment Activity Log entry β sync handles the machine-readable state, the activity log handles the human-readable "why."
Updated 23 days ago
