Developer reference

REST API

A small, predictable API for reading and creating workspace data. Scale tier. Bearer-token auth. JSON in, JSON out.

Auth

Generate keys in Settings → API Keys. Each key looks like ck_live_xxxxxxxx_… and is shown once at creation. Send it as the standard Authorization header on every request:

Authorization: Bearer ck_live_xxxxxxxx_…

Rate limit: 120 requests per minute, per key. Every response carries RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset headers (reset in seconds), and a 429 adds Retry-After — throttle on those rather than counting requests yourself. Optionally restrict by IP allowlist in the key settings.

Base URL

https://rebaar.in/api/v1

Machine-readable spec: https://rebaar.in/openapi.json (OpenAPI 3.0 — typed schemas and operation IDs for codegen and AI function calling).

Endpoints

Projects

MethodPathDescription
GET/projectsList projects (cursor-paged)
GET/projects/{id}Get one project + linked client
POST/projects/createCreate a project (subject to plan limit). Optional pincode, stateCode (2-digit GST code, e.g. 29) and district: the PIN fills the state and district when they are not sent, and a state the PIN rules out is 400 with field: "pincode" — for the site, whose state an invoice suggests as place of supply

Invoices

MethodPathDescription
GET/invoicesList invoices, filterable by ?status=
GET/invoices/{id}Get one invoice + linked client + project
POST/invoices/createCreate an invoice as Draft (default) or Sent. Amounts in paise (₹ × 100)
PATCH/invoices/{id}Update status / amountPaid / paidAt / dueDate, under the same rules as the app: an issued invoice never goes back to Draft; Cancelled is final; a Paid invoice’s status follows its payments; a Draft moves only to Sent (not while approvals are pending) or Cancelled; cancelling needs the IRN cancelled and nothing paid. Raising amountPaid — or sending status: "Paid", meaning paid in full — records a payment for the difference, which shows (and can be voided) in Rebaar; the same call twice records it once. amountPaid can only go up (409 amount_paid_decrease), cannot exceed the total (422), and sets the status (Partial / Paid) unless you pass a status that agrees. Partial is never set on its own — it follows the payments. Refusals are 409 with { error, message }
DELETE/invoices/{id}Delete — only a Draft with nothing paid, no IRN and no payment awaiting verification (else 409). An issued invoice is cancelled with PATCH, never deleted

Vendors

MethodPathDescription
GET/vendorsList vendors
GET/vendors/{id}Get one vendor
POST/vendors/createCreate a vendor. Optional pincode, stateCode (2-digit GST code, e.g. 29) and district: the PIN fills the state and district when they are not sent, and a state the PIN rules out is 400 with field: "pincode"
PATCH/vendors/{id}Update contact / classification / location fields. A new pincode decides its own state and district
DELETE/vendors/{id}Delete vendor

Clients

MethodPathDescription
GET/clientsList clients
GET/clients/{id}Get one client
POST/clients/createCreate a client. Optional pincode, stateCode (2-digit GST code, e.g. 29) and district: the PIN fills the state and district when they are not sent, and a state the PIN rules out is 400 with field: "pincode"
PATCH/clients/{id}Update name / contact / GSTIN / location. A new pincode decides its own state and district
DELETE/clients/{id}Delete — refuses with 409 if the client still has invoices, projects, or quotations

Need an endpoint that isn't listed? Subscribe to the outbound webhooks for event notifications, or email [email protected].

Example

curl https://rebaar.in/api/v1/projects \
  -H "Authorization: Bearer ck_live_xxxxxxxx_…"

Response (shortened):

{
  "object": "list",
  "data": [
    {
      "id": "casa-verde",
      "name": "Casa Verde",
      "type": "Residential",
      "status": "On Track",
      "value": 8750000,             // value in paise (₹87,500)
      "progress": 72,
      "client": { "id": "marigold-estates", "name": "Marigold Estates" }
    }
  ],
  "hasMore": false
}

Webhooks

Outbound webhooks fire on workspace events: invoice.created, invoice.paid, invoice.overdue, project.created, project.status_changed, lead.created, lead.won, quotation.approved, quotation.rejected.

Configure your endpoint URL in Settings → Webhooks. Every request is signed following the Standard Webhooks specification:

webhook-id:        <unique per message; REUSED across retries — use as your idempotency key>
webhook-timestamp: <unix seconds, when THIS attempt was sent>
webhook-signature: v1,<base64 HMAC-SHA256>
x-rebaar-event:    invoice.paid   (convenience only — NOT signed; never branch on it before verifying)

The signed content is {webhook-id}.{webhook-timestamp}.{raw body}, joined by full stops. Use the raw request body — parsing and re-serialising changes the bytes and the signature will not match. Reject anything whose timestamp is more than 5 minutes from your clock, in either direction; that is what stops a captured request being replayed later.

const crypto = require('crypto')

function verify(req, rawBody, secret) {
  const id = req.headers['webhook-id']
  const ts = Number(req.headers['webhook-timestamp'])
  if (!Number.isFinite(ts)) return false
  if (Math.abs(Math.floor(Date.now() / 1000) - ts) > 300) return false   // replay window

  const expected = 'v1,' + crypto
    .createHmac('sha256', secret)
    .update(id + '.' + ts + '.' + rawBody)
    .digest('base64')

  // Any entry may match — the header is a space-delimited list so we can rotate secrets.
  return String(req.headers['webhook-signature'])
    .split(' ')
    .some(sig => sig.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
}

We retry failed deliveries with exponential backoff up to 5 attempts, then move them to the dead-letter queue. A retry keeps the same webhook-id but carries a fresh webhook-timestamp and signature, so verification succeeds however late it arrives — deduplicate on webhook-id.

Errors

401  Authorization header missing or invalid
402  Plan doesn't include this — currentPlan / requiredPlan in body
402  Plan limit reached — used / limit in body
402  Workspace access expired — trial / past_due / cancelled / suspended
404  Resource not found
429  Rate limited (120 req/min) — Retry-After header set
5xx  Internal — retry with exponential backoff