Resources / Developers
Connect your systems to Shipment 360.
Send your website's leads to CRM. Bring in WhatsApp, email and SMS. Read and write Books from your own server.
Get Shipment 360curl -X POST https://api.keeplee.com/v1/crm/leads \
-H "Authorization: Bearer kos_pub_…" \
-H "Origin: https://www.yourcompany.com" \
-H "Content-Type: application/json" \
-d '{"name":"Customer name","phone":"+91 90000 00000",
"message":"Rate for 2 x 20 ft to Jebel Ali","form":"contact"}'
{"data":{"received":true}}
Start here
The address is https://api.keeplee.com. Every call is over HTTPS, and sends and receives JSON. A key belongs to one company, so the key says whose data a call is about.
- Make a key for the one thing you want to do. Each company makes its own.
- Copy it right away. We show it only once.
- Send your first call, like the one above.
Keys
Make keys for CRM on its Channels page. Make keys for Books in Books, Settings, API keys. We store only a SHA-256 of a key, so we cannot show it to you again. If you lose a key, revoke it and make another. Revoking stops a key at once. A key does one action.
Public
kos_pub_…
A website form. It can only add a lead, and only from the websites you list for it.
Authorization: Bearer, X-Api-Key, or a key fieldSecret
kos_live_…
Your own server. So far it is for Books, read only or read and write. Never put it in a web page.
Authorization: BearerWebhook
kos_hook_…
A service sending messages. It only brings data in.
In the address: /v1/hooks/{key}Answers and limits
A good answer is {"data":…}. A refusal is {"error":{…}} with a code, a message and, where it helps, a message for each field. An unexpected failure is a 500 with a reference. Send it to us, and we can find the failure.
HTTP 422
{"error":{
"code": "validation_failed",
"message": "Please check the fields.",
"fields": { "name": "…" }
}}
Limits are per minute. A public key allows 10 per visitor and 120 in all. A webhook key allows 600. After 30 bad keys from one visitor, we refuse that visitor. The X-RateLimit-* headers report the nearest limit, and a refusal carries Retry-After.
CRM: website leads
Make a website key and list the websites allowed to use it. The key is public, because it must be in the page's script. It can only add a lead, and only from those websites.
POST /v1/crm/leads
- Required:
name, and eitheremailorphone. - Kept in their own fields:
company,city,message,form(orsource) andpage. - Anything else: up to 20 more fields, such as
industry, are kept with the lead.
<form id="contact">
<input name="name" required>
<input name="email" type="email">
<input name="phone">
<textarea name="message"></textarea>
<!-- Hidden from people. A robot fills it, and nothing is kept. -->
<input name="website" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">
<button>Send</button>
</form>
<script>
document.getElementById('contact').addEventListener('submit', function (e) {
e.preventDefault();
var data = Object.fromEntries(new FormData(this));
data.form = 'contact';
data.page = location.href;
fetch('https://api.keeplee.com/v1/crm/leads', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer kos_pub_…'},
body: JSON.stringify(data)
});
});
</script>
No script? Post an ordinary form with hidden key and redirect fields. The visitor returns to redirect with received=1 or error=1, if that address is on an allowed website.
CRM: channels
Messages arrive at one address, /v1/hooks/{key}. A message from someone with an open lead joins that lead. Otherwise, WhatsApp and SMS make a new lead on their own. Email and comments wait in the CRM's Inbox for a person to handle. You can change this for each channel. A message seen before does nothing, so a service can safely send it twice. For each channel, make a key of the right kind on the Channels page.
You need: A Meta business account, a WhatsApp Cloud API number and a Meta app.
In the Meta app, set the callback address to the webhook address and the verify token to the key. Subscribe to messages. Every message must carry Meta's signature, or we refuse it. We keep the text. For a photo, video or document, we keep only its type and caption.
You need: A mail-receiving service, such as Mailgun routes, Postmark or SendGrid Inbound Parse.
Forward your leads mailbox to the service, and give it the webhook address. Fields read: from, From or sender; subject or Subject; text, stripped-text, body-plain, TextBody or body; message_id, Message-Id or MessageID.
SMS
You need: A provider with an inbound number.
Give the provider the webhook address, by POST or GET. Fields read: from, From, sender, mobile or msisdn; text, Body, message or content; id, MessageSid, message_id or requestId.
Facebook and Instagram comments
You need: A Meta app with access to the Page or Instagram account.
Set the callback and verify token as for WhatsApp, and subscribe to the Page's feed and Instagram's comments. A comment carries a name and a link, but no email or phone.
Books API
Use a secret key, read only or read and write, sent as Authorization: Bearer. Dates are YYYY-MM-DD. A company with more than one set of books names it with book=ID. Lists give up to 200 at a time with per_page.
A write follows the same rules as the screen: the locks, the numbering, the approvals and the audit trail. The API can read and add. It does not change or delete.
| GET | /v1/books/books | The sets of books in the company |
| GET | /v1/books/ledgers | Ledgers: q, page, per_page |
| GET | /v1/books/ledgers/{id} | One ledger with its balance: as_at |
| POST | /v1/books/ledgers | Add a ledger: name, group_id and the fields of the form |
| GET | /v1/books/vouchers | The Day Book: from, to, type, page |
| GET | /v1/books/vouchers/{id} | One voucher with its lines |
| POST | /v1/books/vouchers | Add a voucher: type, date, narration, lines |
| GET | /v1/books/invoices | Invoices: direction, from, to, status, page |
| GET | /v1/books/invoices/{id} | One invoice, its lines and what is pending |
| POST | /v1/books/invoices | Add an invoice: a draft, or posted with issue: true |
| POST | /v1/books/receipts | Record a receipt: party_ledger_id, amount, date, bank_ledger_id, narration |
| GET | /v1/books/outstandings | What is owed: kind, as_at |
| GET | /v1/books/trial-balance | The trial balance |
Sending it twice is safe. Add an Idempotency-Key to a write. We keep the first answer for 24 hours. A retry gets that answer, not a second invoice.
curl -X POST https://api.keeplee.com/v1/books/invoices \
-H "Authorization: Bearer kos_live_…" \
-H "Idempotency-Key: my-system-invoice-1042" \
-H "Content-Type: application/json" \
-d '{"direction":"sales","contact_id":12,"invoice_date":"2026-11-06","issue":true,
"lines":[{"charge_head_id":3,"description":"Ocean freight",
"quantity":2,"unit":"CTR","rate":45000}]}'
Not here yet
So you can plan, here is what the API does not do today. There are no sign-in tokens for acting as a user. It does not send webhooks out to your system. And Job Board, HR & Payroll, Inventory, Asset Management and BI Dashboard have no API. If you need one of these, tell us.
Questions developers ask
Does Shipment 360 have an API? 
Yes. It brings leads and messages into CRM, and it reads and writes Books. The other parts do not have an API yet.
How do I send website leads to the CRM? 
Make a website key on the Channels page in CRM, and list your websites. Then post each form to /v1/crm/leads. The sample on this page is complete.
Can I connect WhatsApp enquiries to the CRM? 
Yes, through the WhatsApp Cloud API. You need your own Meta business account, a number and a Meta app. Then each new message makes a lead, or joins the sender's open lead.
Can my system post invoices to Books? 
Yes, with a secret key that can read and write. Tax is worked out by the same code as the form on the screen. The same locks and rules apply.
Can I change or delete a record through the API? 
No. The API can only read and add. You make changes and deletions on the screen, where the audit trail records them.