Brand API
Send us borrowers, and we handle the rest: a letter of demand approved by a lawyer, served, and followed up on Day 7 and Day 14. We tell your system each step, and you can download the letter once it’s approved.
1. Keys
Pandas LP gives you two keys. Send one on every call as Authorization: Bearer <key>. Keep them secret; if one leaks, ask us to revoke it and issue a new one.
- Test key (
plp_test_…): for building your integration. Nothing real happens (see Test mode). - Live key (
plp_live_…): real letters, served on real borrowers, billed monthly. Issued once your integration has passed with the test key.
Each key sees only its own borrowers. Limit: 60 calls a minute per key (above that you get 429 with a Retry-After header). Errors always look like { "error": { "code": "…", "message": "…" } }.
2. Send a borrower
POST/borrowers
The same fields as our Google Sheet, plus your own loan_id. Documents (the loan agreement and/or statement) are PDF, JPG or PNG, under 1 MB in total per borrower, each either inside the JSON as base64 or as an https link we download once.
curl -X POST https://pandaslp.com.ng/api/v1/borrowers \
-H "Authorization: Bearer plp_test_..." \
-H "Content-Type: application/json" \
-d '{
"loan_id": "LN-2026-0042",
"borrower_full_name": "Adebayo Ogunleye",
"phone_number": "08031234567",
"email": "adebayo@example.com",
"address_for_service": "12 Isokan Street, Olosan, Ibadan, Oyo State",
"loan_amount": 500000,
"amount_outstanding": 380000,
"loan_date": "2026-03-15",
"last_payment_date": "2026-06-01",
"notes": "Promised to pay in July; has not.",
"documents": [
{ "filename": "loan-agreement.pdf", "content_base64": "JVBERi0xLjQK..." },
{ "filename": "statement.pdf", "url": "https://files.example.ng/LN-2026-0042.pdf" }
]
}'Every borrower is checked straight away, and the answer comes back in the same response:
{
"test": true,
"results": [
{ "loan_id": "LN-2026-0042", "case_id": "ACME-0004", "status": "new" }
]
}status: "new": accepted and queued. Keep ourcase_id, or keep using yourloan_id.status: "missing_documents"withmissing: exactly what to fix. Resend the loan with the fix, or add a note.error: the borrower couldn’t be accepted at all (an unknown field, noloan_id…). Fix and resend.
Several at once: send { "borrowers": [ … ] } with up to 50. Keep each request under 5 MB: with documents inside the JSON that’s about 3 borrowers per call; with links, up to 50. Each borrower gets its own result, in the order sent.
Never twice: sending the same loan_id again (a retry, say) returns the same case with duplicate: true. Before we start on a borrower, a resend with changed details corrects it (updated: true). After we’ve started, changes aren’t applied: they come back in flagged and our staff decide. Send updates as notes instead.
3. Check a borrower
GET/borrowers/{case_id or loan_id}
curl https://pandaslp.com.ng/api/v1/borrowers/LN-2026-0042 -H "Authorization: Bearer plp_test_..."{
"case_id": "ACME-0004", "loan_id": "LN-2026-0042", "status": "served", "test": true,
"missing": [], "letter_available": true,
"served_at": "2026-10-12T09:30:00.000Z", "day7_sent_at": null, "day14_sent_at": null,
"awaiting_decision_since": null, "created_at": "…", "updated_at": "…"
}| Status | Meaning |
|---|---|
new | Accepted and queued for drafting. |
checking | Being checked and drafted right now. |
missing_documents | Something is missing or wrong. `missing` says exactly what; resend the loan or add a note. |
letter_drafted | Drafted, waiting for a lawyer. |
lawyer_approved | A lawyer approved the letter. You can download it. |
served | Served on the borrower. The 14-day response period has started. |
day7_sent | Day 7 reminder sent to the borrower (you are copied). |
day14_sent | Day 14 reminder sent. After this we need your decision: court or close. |
paid | The borrower paid. |
disputed | The borrower disputes the debt. |
closed | Closed. |
4. Download the letter
GET/borrowers/{id}/letter
A PDF, available once a lawyer has approved the letter (before that: 409 letter_not_available). Before serving it’s the approved letter; once served, it’s the stamped copy that was served. Two headers tell you which you have: X-Letter-Version (approved or served) and X-Letter-Stamped (yes or no).
curl -OJ https://pandaslp.com.ng/api/v1/borrowers/ACME-0004/letter -H "Authorization: Bearer plp_live_..."5. Add a note
POST/borrowers/{id}/notes
Notes are added with the date, never replaced. Before we start on a borrower, a note becomes part of their details, and a borrower in Missing Documents is checked again. After that it goes to our staff (for example: “the borrower called and paid half”).
curl -X POST https://pandaslp.com.ng/api/v1/borrowers/LN-2026-0043/notes \
-H "Authorization: Bearer plp_live_..." -H "Content-Type: application/json" \
-d '{ "note": "Correct email is adebayo.o@example.com" }'
{ "case_id": "ACME-0005", "status": "new", "reopened": true, "used_for_letter": true }6. Updates to your system (webhooks)
Give us one https address for live borrowers and one for test borrowers. We send a signed POST each time a borrower moves on:
borrower.missing_documents: withmissingborrower.letter_draftedborrower.lawyer_approved: the letter can be downloaded (letter_url)borrower.served: withserved_atborrower.day7_sentandborrower.day14_sentborrower.awaiting_decision: Day 14 has passed; we need your decision (court or close)borrower.outcome:paid,disputedorclosed
POST https://your-system.example/pandas-webhooks
X-PandasLP-Event: borrower.served
X-PandasLP-Delivery: 6f1c2c1e-6a43-4bd5-9a0e-0d6c1d2f9b11
X-PandasLP-Signature: t=1791797400,v1=5f2b…
{
"id": "6f1c2c1e-6a43-4bd5-9a0e-0d6c1d2f9b11",
"event": "borrower.served",
"occurred_at": "2026-10-12T09:30:00.000Z",
"test": false,
"data": {
"case_id": "ACME-0004", "loan_id": "LN-2026-0042", "status": "served",
"served_at": "2026-10-12T09:30:00.000Z",
"letter_url": "https://pandaslp.com.ng/api/v1/borrowers/ACME-0004/letter"
}
}Answer 2xx within 10 seconds. Anything else is retried after 1, 5, 15, 30, 60, 120, 240, 480, 480 minutes (just under 24 hours), then we stop and our staff contact you. An update may occasionally arrive twice: the id is the same, so ignore one you’ve already handled. You can always check a borrower with GET /borrowers/{id}.
Check the signature with the webhook secret we gave you, on the raw body, and refuse calls more than 5 minutes old:
// Node.js
const crypto = require('crypto')
function isFromPandas(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false
const expected = crypto.createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'))
}# Python
import hmac, hashlib, time
def is_from_pandas(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])7. Test mode
With the test key, everything is checked exactly as it will be live, but nothing real happens: no lawyer, no serving, no emails, no bill.
- A complete test borrower walks the whole journey by itself, one step about every 15 minutes: drafted, approved, served, Day 7, Day 14 (awaiting decision), closed. Each step sends its webhook to your test address.
- Its letter is a SAMPLE, from the same step a real one would be.
- Don’t want to wait? Take the next step at once:
POST/borrowers/{id}/advance
curl -X POST https://pandaslp.com.ng/api/v1/borrowers/LN-TEST-1/advance -H "Authorization: Bearer plp_test_..."
{ "case_id": "ACME-0001", "from": "letter_drafted", "status": "lawyer_approved" }To test Missing Documents, send a borrower with a gap (no email, say). You get your live key once you’ve sent a test borrower, taken one through to Day 14, downloaded its letter, and received a webhook.
8. Everything else
The full description is /api/v1/openapi.json (OpenAPI 3.1). Import it into Postman, Swagger UI or a code generator. Questions: pandaslegalpractitioners@gmail.com.