Skip to main content
Developers

API Documentation

Integrate Rentari.ai's property intelligence engine directly into your enterprise software, custom dashboards, or automated workflows.

AI proposes the best matching section. You decide if it answers your question.

Getting Started

Every Rentari.ai API request talks to https://www.rentari.ai/api/v1/external over HTTPS, sends and receives JSON, and is scoped to a single landlord portfolio. Three steps to make your first call:

  1. 1

    Generate an API key

    Open the Developer dashboard, submit a short access request, and once approved click "Create key". Test keys start with sk_test_, live keys with sk_live_. Store the key in your secret manager. We never show it again.

  2. 2

    Send the bearer header

    Add Authorization: Bearer <your_key> to every request. Missing or malformed headers return HTTP 401.

  3. 3

    Make a test call

    Hit GET /api/v1/external/properties with a test key. It returns the demo portfolio, so you can integrate before any real data exists.

Authentication

The Rentari.ai API uses API keys to authenticate requests. You can view and manage your API keys in the Landlord Settings Dashboard. Your API keys carry many privileges, so be sure to keep them secure!

curl https://www.rentari.ai/api/v1/external/properties \
  -H "Authorization: Bearer sk_live_your_api_key_here"

List Properties

Returns a paginated list of all properties within your portfolio. The properties are returned sorted by creation date, with the most recently created properties appearing first.

GET /api/v1/external/properties
curl https://www.rentari.ai/api/v1/external/properties?limit=20 \
  -H "Authorization: Bearer sk_live_your_api_key_here"
Example Response
{
  "object": "list",
  "data": [
    {
      "id": "prop_8x9a2",
      "street_address": "123 Smart Way",
      "city": "Creve Coeur",
      "state": "MO",
      "unit_count": 4
    }
  ]
}

Generate Lease

Dynamically generate a state-specific lease agreement using our AI Legal Engine. Accepts the property ID and tenant details to return a customized, ESIGN-ready PDF document URL.

POST /api/v1/external/leases/generate
curl -X POST https://www.rentari.ai/api/v1/external/leases/generate \
  -H "Authorization: Bearer sk_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "property_id": "prop_8x9a2",
    "tenant_id": "tnt_12fa7",
    "start_date": "2026-06-01",
    "monthly_rent": 1850
  }'
Example Response
{
  "id": "lease_4q1z8",
  "status": "draft",
  "document_url": "https://cdn.Rentari.ai/leases/lease_4q1z8.pdf",
  "esign_url": "https://www.rentari.ai/sign/lease_4q1z8",
  "expires_at": "2026-06-08T00:00:00Z"
}

Webhooks

Subscribe to real-time events to keep your internal CRM or accounting software perfectly synced with Rentari.ai. Endpoints must be HTTPS and return a 200 status code quickly.

lease.signed

Fired when all parties have securely e-signed an agreement.

payment.succeeded

Fired when a rent ACH or Credit Card payment successfully clears.

maintenance.created

Fired when a tenant submits a new AI-triaged repair ticket.

Webhook Signing

Every webhook delivery includes an X-Rentari.ai-Signature header. Verify it before trusting the payload. The signature is the hex HMAC-SHA256 of timestamp.body using your endpoint signing secret (shown once when you create the endpoint). Reject requests older than 5 minutes to defeat replay attacks.

import hmac, hashlib, time

def verify(secret, raw_body, header):
    ts, sig = header.split(",")
    ts = ts.split("=")[1]
    sig = sig.split("=")[1]
    if abs(time.time() - int(ts)) > 300:
        raise ValueError("stale webhook")
    expected = hmac.new(
        secret.encode(), f"{ts}.{raw_body}".encode(), hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(expected, sig):
        raise ValueError("bad signature")

Rentari.ai retries failed deliveries with exponential backoff for 24 hours. Return any 2xx to acknowledge.

Errors

Rentari.ai uses standard HTTP status codes. All error responses share the same envelope so you can write one handler:

Error Envelope
{
  "error": {
    "type": "validation_error",
    "code": "invalid_field",
    "message": "monthly_rent must be a positive number.",
    "param": "monthly_rent",
    "request_id": "req_8x9a2b1c"
  }
}
StatusTypeWhen you'll see it
400validation_errorBody or query failed schema validation. Check param.
401authentication_errorMissing, malformed, or revoked API key.
403permission_errorKey is valid but lacks the required scope or plan tier.
404not_foundResource id does not exist in your portfolio.
409conflictIdempotency replay with a different body, or stale write.
429rate_limit_exceededBack off, then retry after the Retry-After header.
5xxapi_errorOur problem. Safe to retry with exponential backoff.

Always log request_id. Our support team can find any request by id within 90 days.

Rate Limits

Limits are per API key, measured over a rolling 60-second window. Every response includes the headers below so you can implement adaptive backoff:

Starter

60 req/min

Burst up to 120, then throttled.

Pro

600 req/min

Burst up to 1,200.

Prime

3,000 req/min

Custom ceilings on request.

Response Headers
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 412
X-RateLimit-Reset: 1717689600
Retry-After: 14

Endpoint Reference

The full surface available today. Every list endpoint follows the pagination contract above; every mutation respects idempotency keys via the Idempotency-Key header.

MethodPathWhat it does
GET/api/v1/external/propertiesList properties in the portfolio.
GET/api/v1/external/properties/{id}Retrieve a single property with units.
GET/api/v1/external/tenantsList tenants in the portfolio.
GET/api/v1/external/leasesList leases with status, dates, and rent.
GET/api/v1/external/maintenance/ticketsList repair tickets, including AI triage category.
GET/api/v1/external/paymentsList rent payments with status and method.
POST/api/v1/external/propertiesCreate a property with a default unit. Needs the write scope.
PATCH/api/v1/external/properties/{id}Update a property address or type. Needs the write scope.
PATCH/api/v1/external/tenants/{id}Update tenant contact details. Needs the write scope.
PATCH/api/v1/external/leases/{id}Update a lease. Changing rent or deposit needs the payments scope.
POST/api/v1/external/maintenance/ticketsOpen a maintenance ticket. Needs the write scope.
PATCH/api/v1/external/maintenance/tickets/{id}Update ticket status, priority, text, or vendor. Needs the write scope.
POST/api/v1/external/webhooks/endpointsRegister a webhook endpoint and receive its signing secret.
GET/api/v1/external/webhooks/endpointsList your registered webhook endpoints.
DELETE/api/v1/external/webhooks/endpoints/{id}Disable a webhook endpoint. Existing history preserved.

Need an endpoint that isn't here yet? Email [email protected] with the use case.

Connect Your Own AI (MCP)

Rentari runs a Model Context Protocol server, so you can point Claude, Cursor, ChatGPT, or any MCP-capable assistant at your own portfolio. The assistant reads your data and, if you allow it, makes changes. It uses the same API key and the same permissions as the REST API above.

Server endpoint

POST https://www.rentari.ai/mcp

Transport: Streamable HTTP (JSON-RPC 2.0). Authenticate with Authorization: Bearer sk_live_..., exactly as with the REST API.

Setting it up

  1. Open your developer dashboard and create an API key.
  2. Pick what the key is allowed to do. Read only is the safe default for an assistant.
  3. Copy the key. It is shown once.
  4. Add the server to your assistant. In Cursor, that means a .cursor/mcp.json entry:
{
  "mcpServers": {
    "rentari": {
      "url": "https://www.rentari.ai/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_YOUR_KEY_HERE"
      }
    }
  }
}

Permissions

Every key carries scopes, and the assistant can never exceed them. Choose the narrowest scope that does the job.

ScopeAllows
readPull properties, tenants, leases, maintenance tickets, and payments. Cannot change anything.
writeAlso update properties, tenants, leases, and maintenance tickets.
paymentsAlso change a lease's rent or security deposit, which changes what a tenant is billed.

Test (sandbox) keys are always read only, no matter which scopes they carry. Revoking a key in the dashboard cuts the assistant off immediately.

Available tools

ToolWhat it doesScope
list_propertiesList properties.read
get_propertyRetrieve one property.read
list_tenantsList tenants.read
list_leasesList leases.read
list_maintenance_ticketsList maintenance tickets.read
list_paymentsList rent ledger rows.read
get_portfolio_summaryCounts across the portfolio.read
update_propertyChange a property address or type.write
update_tenantChange tenant contact details.write
create_maintenance_ticketOpen a maintenance ticket.write
update_maintenance_ticketChange ticket status, priority, text, or vendor.write
update_leaseChange lease dates or status. Rent and deposit need payments.write

Preview before changing

Every write tool accepts dry_run. With dry_run: true the server validates the change and returns exactly what the record would look like, without saving. The server instructs connected assistants to preview first and ask you before committing, so you see the change described in plain language before it happens.

{
  "name": "update_maintenance_ticket",
  "arguments": {
    "ticket_id": "abc123",
    "status": "completed",
    "dry_run": true
  }
}

Limits

MCP calls count against the same per-key rate limits as the REST API, and responses carry the same X-RateLimit-* headers. A single request may carry a batch of up to 20 messages, and each message in the batch counts separately. Assistants tend to be chattier than hand-written integrations, so watch the remaining-call header if you are on the free tier.