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.