Skip to content

Errors ​

Two places things fail ​

This is the distinction that matters most, and it is easy to miss.

Synchronous errors happen at the API boundary. Bad credential, missing parameter, unauthorized company. You get an HTTP error immediately.

Asynchronous failures happen at the Tally boundary. The request was valid, the job ran, and Tally refused it. You get success: true first, and only discover the failure when you check the transaction.

An integration that only handles the first kind will appear to work and will silently lose writes.

HTTP status codes ​

StatusMeaningUsual cause
200Accepted or returnedFor writes: queued, not completed
400Bad requestMalformed payload, missing required field
401UnauthenticatedWrong or malformed credential
403UnauthorizedKey valid, but not for this company
404Not foundWrong identifier, or a resource outside your scope
422Validation failedPayload well-formed but semantically invalid
429Too many requestsBack off
5xxServer errorRetry with backoff

401 vs 403 ​

Worth separating, because the fixes are unrelated.

401 — the credential itself. Check the header format is Bearer {key_id}:{secret}, with no trailing whitespace, and that the key has not been revoked.

403 — the credential is fine, the relationship is not. Authorization requires key → application → customer → company. A 403 almost always means the company is not attached to a customer under your application, or you sent a company_id belonging to a different application.

Sending a Tally company GUID where company_id is expected also lands here.

Failed transactions ​

json
{
  "success": true,
  "transaction": {
    "job_id": "11111111-1111-1111-1111-111111111111",
    "transaction_id": "invoice-2026-0042",
    "status": "failed",
    "retry_count": 1,
    "max_retries": 5,
    "error_code": "TALLY_IMPORT_FAILED",
    "error_label": "Tally import failed",
    "error_message": "Ledger 'Online Sales' does not exist!",
    "payload": {
      "voucher_number": "INV-0042",
      "voucher_type": "Sales"
    }
  }
}

success: true means the status query succeeded; transaction.status: "failed" means the work failed. Branch on transaction.status, not on success.

error_code is the stable machine-readable value, error_label is suitable for display, and error_message is a sanitized human-readable reason. For a Tally rejection, Bizmitra extracts the useful message from Tally's response; raw XML, stack traces, secrets, and internal service details are not part of the public contract.

Common causes, in rough order of frequency:

CauseFix
A named master does not existCreate it, or correct the name. See Masters.
Master name mismatch — case, trailing spaceMatch the company exactly
Statutory validationCorrect the gst block; check the company has GST enabled
Unbalanced entriesDebits and credits must net correctly
Voucher type not configuredThe company has no such voucher type
Connector offline mid-jobRetry once it is back

Diagnosing in order ​

Work down this list rather than starting with the payload. The payload is usually the last thing wrong.

  1. GET /api/v1/ping — is the credential valid?
  2. GET /api/v1/companies/{id}/health — is the Connector online and Tally reachable?
  3. GET /api/v1/connectors/{machine}/jobs — what has this connector actually been doing?
  4. GET /api/v1/transactions/{id} — what did Tally say about this specific job?
  5. The payload — do all named masters exist, exactly as written?

Most "the API is broken" reports resolve at step 2.

Retry policy ​

FailureRetry?
5xx, 429Yes, exponential backoff with jitter
Network timeout on a writePoll first. The job may have succeeded.
401, 403No. Fix the credential or provisioning.
400, 422No. Fix the payload.
Transaction failed on validationNo, not blindly. Fix the cause, then resubmit.

That second row is the important one. A timeout tells you nothing about whether the work happened. Retrying immediately is how duplicate vouchers get created. Poll the transaction, and only resubmit if it genuinely is not there.

Never build an unbounded automatic retry against a validation failure. Tally rejected the payload once and will reject it identically forever, while generating noise in the customer's system and yours.

What to log ​

  • transaction_id for every write, before anything else.
  • key_id — never the secret.
  • company_id, and the request path.
  • The public transaction response (error_code, error_label, and error_message) for failed transactions.

When a customer disputes something six months later, these identifiers and public failure fields provide a safe audit trail without storing internal diagnostics in application logs.

Documentation content is all rights reserved; code samples are MIT licensed. Use of the Bizmitra API, Connector App, and hosted services is governed separately by Bizmitra’s commercial terms.