What you can build

The APIs are building blocks. Sync is one thing we built with them; what you build is up to you. Each example below uses real endpoints and field names, and every call acts as the person who signed in, inside their role and plan.

  • Amounts are whole numbers in the smallest unit (paise, cents).
  • token is the access token from signing in; add x-sparky-org: ORG_ID if you belong to several organizations.
  • Each field, answer and error is on the page for that API under the list of APIs.

Send invoices from your own system

Your billing system knows who owes what. Let it write the drafts in Sparky Invoices every month.

Your billing systemSparky InvoicesYour team reviews and sends

// every month, for each customer in your own system
for (const c of customers) {
  await fetch('https://invoices.sparkyli.com/api/v1/invoices', {
    method: 'POST',
    headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json',
               'idempotency-key': `${month}-${c.id}` },
    body: JSON.stringify({
      client_id: c.sparkyClientId,
      due_date: dueDate,
      line_items: [{ description: `Support plan, ${month}`, unit_price: c.amount, tax_rate: 18 }],
    }),
  });
}

A repeatable key means running the job twice never bills anyone twice. The drafts wait for a person to review and send.

Keep Sparky Contacts as your CRM

Your website, app or spreadsheet stays the front door. Sparky Contacts is where every person is kept and found.

Your sign-up formSparky ContactsYour sales page looks them up

// the form on your site saves the lead
await fetch('https://contacts.sparkyli.com/api/v1/contacts', {
  method: 'POST',
  headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
  body: JSON.stringify({ name, email, company }),
});

// your own sales screen finds them again
const r = await fetch('https://contacts.sparkyli.com/api/v1/contacts?q=acme', {
  headers: { authorization: `Bearer ${token}` },
});
const { contacts } = await r.json();

Add people from anywhere and search them from anywhere, with the same permissions they have in the app.

From first enquiry to invoice

Chain the products your own way: a person enters through your application and ends up billed, with no one re-typing them.

Your applicationSparky ContactsSparky Invoices clientDraft invoice

const call = (url, body) => fetch(url, { method: 'POST', body: JSON.stringify(body),
  headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' } }).then((r) => r.json());

const { contact } = await call('https://contacts.sparkyli.com/api/v1/contacts', { name, email, company });
const { links }   = await call('https://invoices.sparkyli.com/api/v1/clients/sync', { contact_id: contact.id });
await call('https://invoices.sparkyli.com/api/v1/invoices', {
  client_id: links[0].client_id,
  line_items: [{ description: 'Discovery workshop', unit_price: 2500000, tax_rate: 18 }],
});

The sync call is the same one the Sync page uses, so a client made this way is a normal client in the app.

An AI agent that works as you

Point an AI assistant at the same APIs. It signs in as you, so it sees what you see and can do what you can do, no more.

"Which invoices are unpaid?"Your AI agentSparky Invoices, in your role

You:    Which of Asha Traders' invoices are still unpaid?

Agent:  (signs in as you, then)
        GET /api/v1/clients               -> finds "Asha Traders"
        GET /api/v1/invoices?client_id=cli_5f0c&status=sent

        Two are open: INV-0031, due 15 Nov, 59,000.00 and
        INV-0036, due 1 Dec, 1,18,000.00 (part paid).

You:    Draft next month's retainer for them.

Agent:  POST /api/v1/invoices -> INV-0042 saved as a draft.
        Review and send it in Sparky Invoices.

An example conversation. No separate login to manage and no extra permissions to grant. Sending, paying and deleting stay with you.

Your own dashboard

Pull time, expenses and invoices into the report, wall screen or spreadsheet your team already likes.

Sparky ProjectsSparky ExpensesSparky InvoicesYour dashboard

const get = (host, path) => fetch(`https://${host}.sparkyli.com/api/v1${path}`,
  { headers: { authorization: `Bearer ${token}` } }).then((r) => r.json());

const [time, spend, open] = await Promise.all([
  get('projects', '/time-entries?from=2026-11-01&to=2026-11-30'),
  get('expenses', '/expenses?status=submitted'),
  get('invoices', '/invoices?status=sent'),
]);
// hours logged, claims waiting, money owed: one screen

Read-only, paged, and limited to what the signed-in person may see, so a manager and an assistant get different answers from the same code.

A website that shows live trips

Keep the design you want and let Sparky Tours supply the journeys, departures and booking terms.

Sparky ToursYour websiteYour visitors

const r = await fetch('https://tours.sparkyli.com/api/v1/tours', {
  headers: { authorization: `Bearer ${token}`, 'x-sparky-org': orgId },
});
const { tours } = await r.json();
// render your own cards: title, length, from-price and the places each journey visits

Classy Explorers, our own travel brand, runs its website this way.

Want help designing something like this? Talk to us.