Rentsi API

Read and write your company's Rentsi data from another app, and get a signed webhook when something happens. OpenAPI 3 spec (JSON)

Quick start

  1. In Rentsi, open Settings → 🧑‍💻 Developers and create an API key. Give it only the access it needs. Copy the key: it is shown once.
  2. Call the API with the key in the Authorization header.
curl https://YOUR-RENTBOOK/api/v1/properties \
  -H "Authorization: Bearer rbk_…"

Every response is JSON. Lists look like {"data":[…],"count":42,"limit":50,"offset":0,"has_more":false,"next":null}; single records like {"data":{…}}.

The base address is your own Rentsi address followed by /api/v1.

Keys & scopes

Send Authorization: Bearer rbk_… on every call. Keys belong to one company: a key only ever sees the company that created it. Keys are stored only as a fingerprint (SHA-256 hash), so Rentsi can't show a key again after it's created. Revoke a key any time in Settings → Developers; it stops working at once.

Each key has access per area, set when it's created: none, read (GET) or read & write (GET, POST, PATCH).

AreaResources
properties/properties
units/units
tenants/tenants
leases/leases
payments/charges and /payments
workorders/workorders
vendors/vendors

GET /api/v1 returns the key's name and scopes, which is handy for checking a key.

Pagination & filters

Lists are sorted by id. Use limit (1–200, default 50) and follow next until it's null. next uses a cursor (the last id you got), so records added meanwhile don't shift pages. offset also works.

FilterWorks onMeaning
property=IDallOnly records in that property
updated_since=2026-10-01T00:00:00ZallRecords changed since (by updatedAt)
since=2026-10-01charges, payments, work orders, leasesDated on or after
lease=IDcharges, paymentsOne lease
status=activeleases, work ordersBy status

Creating & changing

POST /api/v1/{resource} creates a record and answers 201. PATCH /api/v1/{resource}/{id} changes only the fields you send. Only the fields listed under each resource can be set; anything else is refused with 400 unknown_field.

curl -X POST https://YOUR-RENTBOOK/api/v1/workorders \
  -H "Authorization: Bearer rbk_…" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7f3c-leak-3b" \
  -d '{"unitId":"u5","issue":"Leak under kitchen sink","priority":"High","category":"Plumbing"}'

Rate limits

120 requests a minute and 3,000 an hour per key. Each response carries X-RateLimit-Limit and X-RateLimit-Remaining; over the limit you get 429 with Retry-After. Every call, read or write, is written to the company's activity log with the key's name.

Errors

Errors look like {"error":{"code":"forbidden","message":"This key has no write access to tenants."}}.

HTTPcodeWhen
400bad_json, unknown_fieldThe body isn't JSON or sets a field that can't be set
401unauthorizedMissing, wrong or revoked key
403forbiddenThe key has no access to that area
404not_foundNo such resource or record
405method_not_allowedFor example PATCH on a payment
422invalidA value failed a check (missing field, unknown unit, closed books…)
429rate_limitedToo many calls

Data that is never sent

The API never returns Social Security numbers, dates of birth, ID or license numbers, tax IDs, bank account, routing or MICR numbers, or card data. Tenants come with contact details only; vendors without tax or banking details.

Resources

Loading the field list…

Webhooks

Add a webhook in Settings → 🧑‍💻 Developers → Webhooks: an https:// address and the events you want. Rentsi sends a JSON POST each time one happens, from the app, the tenant portal, imports or the API.

EventWhen
tenant.createdA tenant is added
payment.receivedA payment is recorded (office, portal or API)
workorder.createdA work order is opened
workorder.updatedA work order's status, vendor, priority, dates or notes change; data.changes lists the fields
lease.createdA lease is added
pingThe “Send test” button
POST /your/endpoint
Content-Type: application/json
RentBook-Event: workorder.created
RentBook-Delivery: whd_…            (unique per attempt)
RentBook-Event-Id: evt_…            (same for retries: use it to skip duplicates)
RentBook-Timestamp: 1760918400
RentBook-Signature: t=1760918400,v1=5f2b…

{"id":"evt_…","type":"workorder.created","created":"2026-10-20T00:00:00.000Z","company":"…",
 "data":{"object":{"id":"wo…","number":1025,"unitId":"u5","issue":"Leak under kitchen sink","status":"Open",…}}}

Verifying signatures

Each webhook has its own signing secret (whsec_…, shown once when you add it). Compute HMAC-SHA256 of timestamp + "." + raw body with the secret and compare it to v1. Reject timestamps older than 5 minutes.

// Node.js
const crypto = require("crypto");
function verify(rawBody, header, secret) {
  const p = Object.fromEntries(header.split(",").map(x => x.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
  const mac = crypto.createHmac("sha256", secret).update(p.t + "." + rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(p.v1));
}
# Python
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
    p = dict(x.split("=", 1) for x in header.split(","))
    if abs(time.time() - int(p["t"])) > 300: return False
    mac = hmac.new(secret.encode(), p["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, p["v1"])

Retries & delivery log

Answer with any 2xx within 10 seconds. Otherwise Rentsi retries up to 7 times in all: right away, then after 1 min, 5 min, 30 min, 2 h, 6 h and 12 h. Redirects are not followed. Every attempt is in the delivery log in Settings → Developers, where the office can view the payload and resend it.

Rentsi API v1