Book a demo Start free trial
Developers

Quickstart

Four steps: get a key, check it with GET /me, create a lead, receive the lead.created webhook. Ten minutes.

1. Get a key

An agency owner or manager opens Settings → API & webhooks → New key, names it, ticks the scopes it needs and confirms their password. The key (lf_live_…) is shown once. Building a product for many agencies? Request sandbox access and you get an lf_test_… key for the sandbox agency instead; every call below works the same.

2. Check it: GET /me

curl https://app.letfold.com/api/v1/me \
  -H "Authorization: Bearer lf_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$client = new \GuzzleHttp\Client(['base_uri' => 'https://app.letfold.com/api/v1/', 'headers' => [
    'Authorization' => 'Bearer ' . getenv('LETFOLD_API_KEY'),
    'Accept' => 'application/json',
]]);
$me = json_decode($client->get('me')->getBody(), true);
echo $me['data']['agency']['name'];
const res = await fetch('https://app.letfold.com/api/v1/me', {
  headers: { Authorization: `Bearer ${process.env.LETFOLD_API_KEY}` },
});
const me = await res.json();
console.log(me.data.agency.name, me.data.key.scopes);
import os, requests
BASE = "https://app.letfold.com/api/v1"
H = {"Authorization": f"Bearer {os.environ['LETFOLD_API_KEY']}"}
me = requests.get(f"{BASE}/me", headers=H).json()
print(me["data"]["agency"]["name"])

The answer names the key, its environment and scopes, and the agency it belongs to:

{
  "data": {
    "object": "api_key",
    "key": { "name": "Website", "prefix": "lf_live_k3j9x2mq", "environment": "live", "scopes": ["leads:write", "properties:read", "..."] },
    "agency": { "id": "9f3c…", "name": "Harbourside Lettings", "...": "..." },
    "sandbox": false,
    "rate_limit": { "per_minute": 600, "writes_per_minute": 100 }
  }
}

3. Create a lead: POST /leads

Needs the leads:write scope and an Idempotency-Key header (any unique string; a UUID is ideal). The lead goes through the same code path as the public enquiry form: it is assigned to whoever has the fewest open enquiries and the team is told. The answer is 202 Accepted with the lead.

curl -X POST https://app.letfold.com/api/v1/leads \
  -H "Authorization: Bearer $LETFOLD_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Priya Raman","email":"[email protected]","phone":"07700 900123",
       "message":"Is the two-bed on Cotham Hill still available?","source":"website",
       "property_id":"3d1f…"}'
$lead = $client->post('leads', [
    'headers' => ['Idempotency-Key' => (string) \Ramsey\Uuid\Uuid::uuid4()],
    'json' => ['name' => 'Priya Raman', 'email' => '[email protected]', 'message' => 'Is it still available?', 'source' => 'website'],
]);
// 202 Accepted
$id = json_decode($lead->getBody(), true)['data']['id'];
const res = await fetch('https://app.letfold.com/api/v1/leads', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LETFOLD_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({ name: 'Priya Raman', email: '[email protected]', message: 'Is it still available?', source: 'website' }),
});
console.log(res.status); // 202
import uuid
r = requests.post(f"{BASE}/leads", headers={**H, "Idempotency-Key": str(uuid.uuid4())},
                  json={"name": "Priya Raman", "email": "[email protected]", "message": "Is it still available?", "source": "website"})
assert r.status_code == 202
lead_id = r.json()["data"]["id"]

4. Receive the webhook

Create an endpoint (Settings → API & webhooks, or POST /webhook-endpoints with webhooks:manage). Subscribe to lead.created, or * for everything. Press Send test to get a ping. Then every new lead arrives as:

POST https://your-app.example/webhooks/letfold
webhook-id: evt_7h2k…
webhook-timestamp: 1762860000
webhook-signature: v1,base64(HMAC-SHA256("{id}.{timestamp}.{body}", secret))

{
  "id": "evt_7h2k…", "type": "lead.created", "timestamp": "2026-11-11T10:00:00Z",
  "api_version": "v1", "agency_id": "9f3c…",
  "data": { "object": "lead", "id": "c0a1…", "name": "Priya Raman", "status": "new", "source": "website", "...": "..." }
}

Verify the signature before you trust the body; code for PHP, Node and Python is on the webhooks page. Answer 2xx within 15 seconds; anything else is retried for up to a day.

Next: conventions (pagination, updated_since, errors) and the full reference.