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 360
curl -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.

  1. Make a key for the one thing you want to do. Each company makes its own.
  2. Copy it right away. We show it only once.
  3. 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 field

Secret

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: Bearer

Webhook

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 either email or phone.
  • Kept in their own fields: company, city, message, form (or source) and page.
  • 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.

WhatsApp

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.

Email

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/booksThe sets of books in the company
GET/v1/books/ledgersLedgers: q, page, per_page
GET/v1/books/ledgers/{id}One ledger with its balance: as_at
POST/v1/books/ledgersAdd a ledger: name, group_id and the fields of the form
GET/v1/books/vouchersThe Day Book: from, to, type, page
GET/v1/books/vouchers/{id}One voucher with its lines
POST/v1/books/vouchersAdd a voucher: type, date, narration, lines
GET/v1/books/invoicesInvoices: direction, from, to, status, page
GET/v1/books/invoices/{id}One invoice, its lines and what is pending
POST/v1/books/invoicesAdd an invoice: a draft, or posted with issue: true
POST/v1/books/receiptsRecord a receipt: party_ledger_id, amount, date, bank_ledger_id, narration
GET/v1/books/outstandingsWhat is owed: kind, as_at
GET/v1/books/trial-balanceThe 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.