A REST API and an MCP server for booking verified immigration lawyers across Spain. Clients always confirm and pay on leyapp.es — your integration never touches money.
The API exposes the same verified-lawyer data as leyapp.es: search, public profiles, reviews, availability, and booking intents. Anonymous access works at low rate limits; an API key raises limits and unlocks keyed endpoints and usage analytics.
No payments through the API
A booking intent is a 15-minute reservation. The client confirms and pays on leyapp.es — your integration never handles money or card data.
Anonymous-friendly
Read endpoints and intent creation work without a key, at conservative per-IP limits. Keys are free and created in your dashboard.
Test mode built in
leyk_test_ keys run the full validation path but create nothing and notify no one. Build against test, ship with live.
Traceable by design
Every response carries X-Request-Id. Quote it to support and we find your exact request.
From zero to a (test) booking intent in four requests. Create a free test key in Dashboard → Developer first.
# 1. Get a test key at leyapp.es/dashboard/developer
export LEYAPP_API_KEY="leyk_test_..."
# 2. Find a lawyer
curl "https://leyapp.es/api/v1/lawyers/search?city=Barcelona&practice_area=immigration" \
-H "Authorization: Bearer $LEYAPP_API_KEY"
# 3. Check availability (id from step 2)
curl "https://leyapp.es/api/v1/lawyers/<lawyer_id>/availability" \
-H "Authorization: Bearer $LEYAPP_API_KEY"
# 4. Create a (test) booking intent
curl -X POST "https://leyapp.es/api/v1/bookings/initiate" \
-H "Authorization: Bearer $LEYAPP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: demo-1" \
-d '{"lawyer_id":"<lawyer_id>","date":"<date>","start_time":"09:00:00","end_time":"10:00:00"}'Send your key as a Bearer token on every request:
Authorization: Bearer leyk_...
Keyed requests get tier limits (dual window: per-minute burst + per-day quota) with X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. On 429, honor Retry-After and retry with jittered exponential backoff.
| Tier | Requests / minute | Requests / day |
|---|---|---|
| free | 60 | 5,000 |
| partner | 300 | 50,000 |
| internal | 1,000 | ∞ |
New keys start on the free tier. Partner tiers are granted by the LeyApp team — contact us through the support form if your integration needs more headroom. Anonymous requests use separate, lower per-IP limits.
Test keys (leyk_test_) behave exactly like live keys — same endpoints, same validation, same response shapes — but nothing real happens: no booking intent is stored, no lawyer is notified, nothing can be confirmed or paid.
Test traffic appears in your dashboard's request log (flagged) but is excluded from daily aggregates. Swap the key for a live one when you ship — no other change needed.
/api/v1/lawyers/searchSearch verified lawyers
Same search engine as leyapp.es. Verified, publicly listed lawyers only. Scope: lawyers:read (anonymous allowed).
| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | No | |
| practice_area | string | No | Slug from GET /practice-areas |
| language | string | No | |
| min_fee_eur | number | No | |
| max_fee_eur | number | No | |
| sort | string (rating | price_asc | price_desc | reviews) | No | |
| page | integer | No | |
| limit | integer | No |
curl "https://leyapp.es/api/v1/lawyers/search?city=Barcelona&practice_area=immigration&language=en" \ -H "Authorization: Bearer $LEYAPP_API_KEY"
/api/v1/lawyers/{lawyerId}Public lawyer profile
Full public profile incl. bio and bar memberships. Bio is user-generated content wrapped in safety fencing for AI consumers. Scope: lawyers:read (anonymous allowed).
| Parameter | Type | Required | Description |
|---|---|---|---|
| lawyerId | string | Yes |
curl "https://leyapp.es/api/v1/lawyers/<lawyerId>" \ -H "Authorization: Bearer $LEYAPP_API_KEY"
/api/v1/lawyers/{lawyerId}/availabilityAvailable consultation slots
Available slots for a date range (max 14 days). Scope: availability:read (anonymous allowed).
| Parameter | Type | Required | Description |
|---|---|---|---|
| lawyerId | string | Yes | |
| start_date | string | No | Default: today |
| end_date | string | No | Default: start_date + 13 days |
curl "https://leyapp.es/api/v1/lawyers/<lawyerId>/availability" \ -H "Authorization: Bearer $LEYAPP_API_KEY"
/api/v1/lawyers/{lawyerId}/reviewsPublic reviews
Recent reviews from verified clients. Comments are UGC with AI-safety fencing. Scope: reviews:read (anonymous allowed).
| Parameter | Type | Required | Description |
|---|---|---|---|
| lawyerId | string | Yes | |
| limit | integer | No |
curl "https://leyapp.es/api/v1/lawyers/<lawyerId>/reviews" \ -H "Authorization: Bearer $LEYAPP_API_KEY"
/api/v1/practice-areasPractice-area hierarchy
Reference data; cache-friendly (24h). Scope: reference:read (anonymous allowed).
curl "https://leyapp.es/api/v1/practice-areas" \ -H "Authorization: Bearer $LEYAPP_API_KEY"
/api/v1/citiesSupported cities and provinces
Reference data; cache-friendly (24h). Scope: reference:read (anonymous allowed).
curl "https://leyapp.es/api/v1/cities" \ -H "Authorization: Bearer $LEYAPP_API_KEY"
/api/v1/bookings/initiateCreate a booking intent
Creates a 15-minute booking intent for a free slot. The USER must open confirmation_url on leyapp.es to confirm and pay — the API never charges anyone. Supports Idempotency-Key (Stripe semantics: 24h replay, 422 on body mismatch). Test keys validate fully but create nothing. Scope: bookings:initiate (anonymous allowed at 5/hour).
{
"lawyer_id": "<lawyer_id from search>",
"date": "2026-09-01",
"start_time": "09:00:00",
"end_time": "10:00:00",
"source": "api",
"agent_name": "MyIntegration"
}curl -X POST "https://leyapp.es/api/v1/bookings/initiate" \
-H "Authorization: Bearer $LEYAPP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"lawyer_id":"<lawyer_id from search>","date":"2026-09-01","start_time":"09:00:00","end_time":"10:00:00","source":"api","agent_name":"MyIntegration"}'POST endpoints can't be executed from the docs — copy the snippet and run it with a test key instead.
/api/v1/booking-intents/{intentId}API key requiredPoll a booking intent
Status of an intent CREATED BY YOUR KEY: pending | confirmed | expired. Requires an API key with bookings:read (not available anonymously).
| Parameter | Type | Required | Description |
|---|---|---|---|
| intentId | string | Yes |
curl "https://leyapp.es/api/v1/booking-intents/<intentId>" \ -H "Authorization: Bearer $LEYAPP_API_KEY"
Subscribe an HTTPS endpoint (Dashboard → Developer → Webhooks) to events for booking intents created with your key. Deliveries follow the Standard Webhooks spec and are retried automatically.
# Delivery headers (Standard Webhooks — standardwebhooks.com):
# webhook-id: unique delivery id (dedup key — at-least-once delivery)
# webhook-timestamp: unix seconds (reject if older than ±5 minutes)
# webhook-signature: v1,<base64 HMAC-SHA256 of "{id}.{timestamp}.{body}">
#
# Payload: { "id": "...", "type": "booking_intent.confirmed",
# "created_at": "...", "data": { "booking_intent_id": "...", "status": "..." } }
# Events: booking_intent.confirmed | booking_intent.expired
# Retries: 1m / 5m / 30m / 2h / 12h, then dead. Endpoint auto-disabled
# after 20 consecutive failures.Prefer talking instead of coding? LeyApp ships an MCP server with 15 tools — search, availability, booking, and (with your consent) your own bookings — for Claude, ChatGPT, Cursor and any MCP-capable client. OAuth consent with granular permission levels; revoke anytime from Dashboard → Developer.
Settings → Connectors → Add custom connector Name: LeyApp URL: https://leyapp.es/api/mcp/mcp Claude opens the LeyApp consent screen — sign in, pick a permission level, and you're connected.
The v1 API is additive-only: fields are added, never renamed or removed. Breaking changes would ship as a new version with a 90-day deprecation window announced here and via Deprecation/Sunset headers.
2026-07-22
Reliability, webhook coverage, and key controls
2026-07-22
Search, profiles, reviews, reference data, intent polling, usage analytics
2026-07-16
API keys, tiered rate limits, idempotency, test mode
2026-03-21
Initial public beta
Questions, higher limits, or partnership ideas? Reach us through the support form — or just create a key and start building.