tallysale.com

Available on: Business and Enterprise plans

The TallySale API lets you create and manage invoices programmatically from your own applications — SaaS platforms, e-commerce sites, ERPs, or any system that needs to issue invoices.

Prerequisites

  • A Business or Enterprise seller level (API access is not available on Starter)
  • A Seller App with API Key and Secret (created by an administrator under Seller Apps)

Authentication

TallySale uses HMAC-SHA256 API key authentication. Every request must include three headers:

Header Description
X-API-Key Your Seller App's app_key (starts with ts_)
X-API-Signature HMAC-SHA256 signature of the request
X-API-Timestamp Current Unix timestamp (seconds)

Signature Calculation

The signature is calculated as:

signature = HMAC-SHA256(app_secret, method + path + timestamp + body)

Where:

  • method — HTTP method in uppercase (GET, POST, PUT, DELETE)
  • path — Request path (e.g., /v1/invoices)
  • timestamp — Same value as X-API-Timestamp header
  • body — Request body as a string (empty string for GET requests)

Security: Never expose your API Secret in client-side code (browser JavaScript, mobile apps). All API calls must originate from your server.

Base URL

https://api.tallysale.com/v1

Endpoints

Invoices

Method Path Description
GET /v1/invoices List all invoices
GET /v1/invoices/{id} Get invoice detail

Invoices listed here are read-only in the admin portal. They are created via the Seller Invoice endpoints below.

Seller Invoices

Method Path Description
GET /v1/seller-invoices List invoices
POST /v1/seller-invoices Create a new invoice
GET /v1/seller-invoices/{id} Get invoice detail
PUT /v1/seller-invoices/{id} Update a draft invoice
POST /v1/seller-invoices/{id}/finalize Finalize (lock for payment)
POST /v1/seller-invoices/{id}/send Send via email
POST /v1/seller-invoices/{id}/void Cancel the invoice
POST /v1/seller-invoices/{id}/mark-paid Record manual payment
POST /v1/seller-invoices/{id}/duplicate Duplicate as new draft
GET /v1/seller-invoices/{id}/pdf Download PDF

Other Endpoints

Method Path Description
GET /v1/seller-invoices/summary Dashboard summary stats
GET /v1/seller-invoices/customers/search Search customers
GET /v1/seller-invoices/payments Payment history
GET /v1/seller-invoices/credit-notes List credit notes

Creating an Invoice — Request

POST /v1/seller-invoices
Content-Type: application/json
{
  "customer_email": "customer@example.com",
  "customer_name": "Aung Aung",
  "customer_phone": "+959123456789",
  "due_date": "2026-07-15",
  "notes": "Thank you for your business",
  "line_items": [
    {
      "description": "Web Development Service",
      "quantity": 1,
      "unit_price": 500000
    },
    {
      "description": "Hosting (Monthly)",
      "quantity": 12,
      "unit_price": 25000
    }
  ],
  "discount_type": "percentage",
  "discount_value": 5
}

Creating an Invoice — Response

{
  "message": "Invoice created successfully",
  "data": {
    "id": "1234567890123456789",
    "invoice_number": "INV-2026-0042",
    "status": "DRAFT",
    "customer_email": "customer@example.com",
    "customer_name": "Aung Aung",
    "subtotal": 800000,
    "discount_amount": 40000,
    "total_amount": 760000,
    "currency": "MMK",
    "due_date": "2026-07-15",
    "source": "api",
    "checkout_url": "https://pay.tallysale.com/c/abc123",
    "created_at": "2026-06-24T12:00:00Z"
  }
}

Code Examples

Node.js

const crypto = require('crypto');
const axios = require('axios');

const API_KEY = 'ts_your_app_key_here';
const API_SECRET = 'your_app_secret_here';
const BASE_URL = 'https://api.tallysale.com';

function createSignature(method, path, timestamp, body = '') {
  const payload = method + path + timestamp + body;
  return crypto
    .createHmac('sha256', API_SECRET)
    .update(payload)
    .digest('hex');
}

async function createInvoice() {
  const method = 'POST';
  const path = '/v1/seller-invoices';
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const body = JSON.stringify({
    customer_email: 'customer@example.com',
    customer_name: 'Aung Aung',
    due_date: '2026-07-15',
    line_items: [
      {
        description: 'Web Development Service',
        quantity: 1,
        unit_price: 500000
      }
    ]
  });

  const signature = createSignature(method, path, timestamp, body);

  try {
    const response = await axios({
      method: method,
      url: BASE_URL + path,
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': API_KEY,
        'X-API-Signature': signature,
        'X-API-Timestamp': timestamp
      },
      data: body
    });

    console.log('Invoice created:', response.data);
    return response.data;
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
  }
}

createInvoice();

PHP

<?php

$apiKey    = 'ts_your_app_key_here';
$apiSecret = 'your_app_secret_here';
$baseUrl   = 'https://api.tallysale.com';

function createSignature(string $method, string $path, string $timestamp, string $body = ''): string {
    global $apiSecret;
    $payload = $method . $path . $timestamp . $body;
    return hash_hmac('sha256', $payload, $apiSecret);
}

function createInvoice(): void {
    global $apiKey, $baseUrl;

    $method    = 'POST';
    $path      = '/v1/seller-invoices';
    $timestamp = (string) time();
    $body      = json_encode([
        'customer_email' => 'customer@example.com',
        'customer_name'  => 'Aung Aung',
        'due_date'       => '2026-07-15',
        'line_items'     => [
            [
                'description' => 'Web Development Service',
                'quantity'    => 1,
                'unit_price'  => 500000,
            ],
        ],
    ]);

    $signature = createSignature($method, $path, $timestamp, $body);

    $ch = curl_init($baseUrl . $path);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $body,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'X-API-Key: ' . $apiKey,
            'X-API-Signature: ' . $signature,
            'X-API-Timestamp: ' . $timestamp,
        ],
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    echo "Status: {$httpCode}\n";
    echo "Response: {$response}\n";
}

createInvoice();

Python

import hmac
import hashlib
import time
import json
import requests

API_KEY = 'ts_your_app_key_here'
API_SECRET = 'your_app_secret_here'
BASE_URL = 'https://api.tallysale.com'


def create_signature(method: str, path: str, timestamp: str, body: str = '') -> str:
    payload = method + path + timestamp + body
    return hmac.new(
        API_SECRET.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()


def create_invoice():
    method = 'POST'
    path = '/v1/seller-invoices'
    timestamp = str(int(time.time()))
    body = json.dumps({
        'customer_email': 'customer@example.com',
        'customer_name': 'Aung Aung',
        'due_date': '2026-07-15',
        'line_items': [
            {
                'description': 'Web Development Service',
                'quantity': 1,
                'unit_price': 500000,
            },
        ],
    })

    signature = create_signature(method, path, timestamp, body)

    response = requests.post(
        BASE_URL + path,
        headers={
            'Content-Type': 'application/json',
            'X-API-Key': API_KEY,
            'X-API-Signature': signature,
            'X-API-Timestamp': timestamp,
        },
        data=body,
    )

    print(f'Status: {response.status_code}')
    print(f'Response: {response.json()}')
    return response.json()


if __name__ == '__main__':
    create_invoice()

Listing Invoices — Example (Node.js)

async function listInvoices(page = 1, perPage = 20) {
  const method = 'GET';
  const path = '/v1/seller-invoices';
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const signature = createSignature(method, path, timestamp);

  const response = await axios({
    method: method,
    url: `${BASE_URL}${path}?page=${page}&per_page=${perPage}`,
    headers: {
      'X-API-Key': API_KEY,
      'X-API-Signature': signature,
      'X-API-Timestamp': timestamp
    }
  });

  console.log(`Found ${response.data.meta.total} invoices`);
  return response.data;
}

Webhooks

Available on: Business and Enterprise plans

If your Seller App has webhooks enabled, TallySale will send HTTP POST callbacks to your backend_callback_url when invoice events occur:

Event Trigger
invoice.paid Customer completes payment
invoice.partially_paid Customer makes a partial payment
invoice.voided Invoice is cancelled
invoice.refunded Credit note issued against the invoice

Webhook Payload

{
  "event": "invoice.paid",
  "timestamp": "2026-06-24T15:30:00Z",
  "data": {
    "invoice_id": "1234567890123456789",
    "invoice_number": "INV-2026-0042",
    "status": "PAID",
    "total_amount": 760000,
    "paid_amount": 760000,
    "currency": "MMK",
    "customer_email": "customer@example.com",
    "payment_method": "kbz_pay"
  },
  "signature": "hmac_sha256_signature_here"
}

Verifying Webhook Signatures

Always verify the webhook signature to ensure the request is from TallySale:

function verifyWebhook(payload, signature) {
  const expected = crypto
    .createHmac('sha256', API_SECRET)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Rate Limits

API requests are rate-limited based on your Seller Level:

Level Read Requests Write Requests
Business 60 / minute 30 / minute
Enterprise Unlimited 120 / minute

Rate-limited responses return HTTP 429 Too Many Requests with a Retry-After header.

Error Responses

All errors follow a standard format:

{
  "message": "Validation error",
  "data": {
    "customer_email": ["The customer email field is required."],
    "line_items": ["At least one line item is required."]
  }
}
HTTP Code Meaning
200 Success
201 Resource created
400 Bad request (invalid input)
401 Authentication failed (invalid API key or signature)
403 Forbidden (insufficient permissions or feature not available)
404 Resource not found
422 Validation error
429 Rate limit exceeded
500 Server error