The Landed API is a REST API for managing data integration pipelines (connectors, destinations, syncs). All endpoints are served over HTTPS and return JSON.
Base URLs
| Environment | URL |
|---|
| Production | https://api.landed.dev |
| Staging | https://api-staging-<hash>.run.app |
| Local | http://localhost:8000 |
Authentication
The API supports three authentication methods, checked in this order:
1. Session Cookie (browser)
After login or OAuth, an landed_session httpOnly cookie is set. The frontend uses this automatically. Sessions expire after 30 days.
2. API Key (programmatic)
API keys use the lnd_ prefix. Pass them as a Bearer token:
Authorization: Bearer lnd_abc123...
Create a key via POST /auth/api-keys. Keys are stored as SHA-256 hashes -- the raw key is returned only once at creation time. Revoke all keys with DELETE /auth/api-keys.
3. JWT (short-lived)
JWTs are issued on login/signup and expire after 1 hour. Refresh them via POST /auth/refresh (requires a valid session cookie). Pass as a Bearer token:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Rate Limits
All endpoints are rate-limited per client IP using slowapi. Limits are specified per endpoint:
| Tier | Limit | Typical endpoints |
|---|
| Read | 60/minute | List/get resources, catalogs |
| Read (high-freq) | 120/minute | Single resource GETs, job polling |
| Write | 20/minute | Create, update, delete |
| Sensitive | 10/minute | Sync triggers, connection tests, OAuth, secret rotation |
| Auth | 5/minute, 20/hour | Signup |
| Auth | 10/minute, 50/hour | Login |
When rate-limited, the API returns 429 Too Many Requests with a Retry-After header.
- Content-Type:
application/json for all request bodies
- Pagination: Most list endpoints accept
limit (default 50, max 200) and offset (default 0) query params
- IDs: All resource IDs are UUIDs (string format)
- Timestamps: ISO 8601 format with timezone (e.g.,
2026-03-28T12:00:00+00:00)
Error Responses
All errors return a JSON body:
{
"error": "Human-readable message",
"detail": "More specific info (optional)"
}
Standard Status Codes
| Code | Meaning |
|---|
| 400 | Bad request -- invalid params, unsupported type, validation failure |
| 401 | Not authenticated -- missing or invalid credentials |
| 402 | Payment required -- connector quota exceeded |
| 403 | Forbidden -- subscription inactive |
| 404 | Resource not found (or does not belong to your account) |
| 409 | Conflict -- duplicate name, already exists |
| 422 | Validation error -- Pydantic model validation failed |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
| 502 | Bad gateway -- upstream provider (Stripe, OAuth) unavailable |
| 503 | Service unavailable -- feature not configured |
Validation Errors (422)
{
"error": "Validation error",
"detail": [
{
"loc": ["body", "name"],
"msg": "String should have at least 1 character",
"type": "string_too_short"
}
]
}
Every response includes:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
X-Request-ID: <uuid> -- correlation ID for debugging (pass your own via the request header to trace)
OpenAPI / Swagger
The interactive API docs are available when EXPOSE_API_DOCS=true:
- Swagger UI:
GET /docs
- ReDoc:
GET /redoc
- OpenAPI JSON:
GET /openapi.json
These are disabled in production by default.
Endpoints
Health
| Method | Path | Auth | Description |
|---|
| GET | /health | None | Liveness + readiness probe. Returns {"status": "ok"} with DB pool stats, or 503 if unhealthy. |
Auth
| Method | Path | Auth | Description |
|---|
| POST | /auth/signup | None | Create account with email + password |
| POST | /auth/login | None | Login with email + password. Account locks after 5 failed attempts for 15 minutes. |
| POST | /auth/logout | Session | Invalidate session, clear cookie |
| POST | /auth/refresh | Session + JWT | Refresh an expired JWT (token must be less than 7 days old, session must be valid) |
| GET | /auth/me | Any | Bootstrap endpoint -- returns customer info + fresh JWT |
| POST | /auth/api-keys | Any | Generate a new API key (returned once) |
| DELETE | /auth/api-keys | Any | Revoke all API keys |
| POST | /auth/exchange-code | None | Exchange a short-lived auth code for JWT (used after OAuth redirect) |
OAuth (Login/Signup)
| Method | Path | Auth | Description |
|---|
| GET | /auth/oauth/google | None | Redirect to Google consent screen |
| GET | /auth/oauth/google/callback | None | Google OAuth callback (internal) |
| GET | /auth/oauth/github | None | Redirect to GitHub consent screen |
| GET | /auth/oauth/github/callback | None | GitHub OAuth callback (internal) |
| POST | /auth/oauth/exchange | None | Exchange provider access token for Landed JWT (used by NextAuth) |
Connectors
| Method | Path | Auth | Description |
|---|
| GET | /connectors/catalog | Optional | List all available connector types with metadata and auth fields |
| GET | /connectors/catalog/{type}/schema | None | Get full stream schema for a connector type |
| GET | /connectors | Required | List configured connectors (paginated) |
| GET | /connectors/summary | Required | Dashboard overview with health counts and sparkline data |
| POST | /connectors | Required | Create a new connector |
| GET | /connectors/{id} | Required | Get connector details |
| PATCH | /connectors/{id} | Required | Update connector (partial) |
| DELETE | /connectors/{id} | Required | Delete connector and all related data |
| POST | /connectors/{id}/sync | Required | Trigger a manual sync |
| POST | /connectors/{id}/test | Required | Test connector credentials (no data written) |
| GET | /connectors/{id}/streams | Required | Get available streams with enabled/disabled state |
| PUT | /connectors/{id}/streams | Required | Update stream selection (enable/disable streams) |
Connector OAuth (Credential Acquisition)
These endpoints handle OAuth flows for connectors that require it. The flow uses a popup window pattern.
| Method | Path | Auth | Description |
|---|
| GET | /connectors/oauth/{provider}/authorize | Required | Get OAuth authorize URL (provider: slack, microsoft, salesforce, hubspot, shopify) |
| GET | /connectors/oauth/{provider}/callback | None | OAuth callback from provider (internal) |
| GET | /connectors/oauth/result | None | Consume short-lived OAuth code to retrieve credentials |
Destinations
| Method | Path | Auth | Description |
|---|
| GET | /destinations/catalog | None | List supported destination types with field definitions |
| GET | /destinations | Required | List configured destinations (paginated) |
| POST | /destinations | Required | Create a destination |
| GET | /destinations/{id} | Required | Get destination details |
| PATCH | /destinations/{id} | Required | Update destination (full body required, type cannot change) |
| DELETE | /destinations/{id} | Required | Delete a destination |
| POST | /destinations/test | Required | Test destination connectivity without saving |
| POST | /destinations/{id}/test | Required | Test an existing destination's connectivity |
Syncs (History)
| Method | Path | Auth | Description |
|---|
| GET | /syncs | Required | List sync receipts (paginated, filterable by connector_id, status, date range) |
| GET | /syncs/stats | Required | Sync statistics with optional filters (connector_id, destination_id, days, granularity) |
| GET | /syncs/{connector_id} | Required | List sync receipts for a specific connector |
| GET | /syncs/{connector_id}/schema | Required | Schema fields, recent changes, and pending backfills |
Jobs (Live Progress)
| Method | Path | Auth | Description |
|---|
| GET | /jobs | Required | List recent jobs (filterable by connector_id, status) |
| GET | /jobs/{id} | Required | Get live job status and progress. Poll while status is pending or running. |
Webhooks
| Method | Path | Auth | Description |
|---|
| GET | /webhooks | Required | List webhook endpoints |
| POST | /webhooks | Required | Create webhook endpoint |
| GET | /webhooks/{id} | Required | Get webhook endpoint |
| PATCH | /webhooks/{id} | Required | Update webhook endpoint |
| DELETE | /webhooks/{id} | Required | Delete webhook endpoint |
| POST | /webhooks/{id}/rotate-secret | Required | Generate new signing secret (returned once) |
| POST | /webhooks/{id}/test | Required | Send a test webhook delivery |
| GET | /webhooks/{id}/deliveries | Required | List delivery log for an endpoint |
| GET | /webhooks/{id}/secret | Required | Retrieve current signing secret (rate-limited) |
See webhooks.md for payload formats and signature verification.
Alerts and Notifications
| Method | Path | Auth | Description |
|---|
| GET | /alerts/groups | Required | List alert groups |
| POST | /alerts/groups | Required | Create alert group |
| GET | /alerts/groups/{id} | Required | Get alert group with connectors |
| PATCH | /alerts/groups/{id} | Required | Update alert group |
| DELETE | /alerts/groups/{id} | Required | Delete alert group |
| POST | /alerts/groups/{id}/connectors | Required | Add connector to group |
| DELETE | /alerts/groups/{id}/connectors/{connector_id} | Required | Remove connector from group |
| GET | /alerts/policies | Required | List notification policies |
| POST | /alerts/policies | Required | Create notification policy |
| PATCH | /alerts/policies/{id} | Required | Update notification policy |
| DELETE | /alerts/policies/{id} | Required | Delete notification policy |
| GET | /alerts/policies/resolve/{connector_id} | Required | Preview which policies apply to a connector |
| POST | /alerts/policies/{id}/test | Required | Send a test notification |
| GET | /alerts/notifications | Required | List in-app notifications |
| POST | /alerts/notifications/{id}/read | Required | Mark notification as read |
| POST | /alerts/notifications/read-all | Required | Mark all notifications as read |
| GET | /alerts/notifications/unread-count | Required | Get unread notification count |
Billing
| Method | Path | Auth | Description |
|---|
| GET | /billing/usage | Required | Connector count, rows synced, monthly estimate |
| POST | /billing/checkout | Required | Create Stripe Checkout session URL |
| POST | /billing/portal | Required | Create Stripe Customer Portal session URL |
| POST | /billing/webhook | None (Stripe signature) | Stripe webhook receiver (internal) |
Integrations (GitHub App)
| Method | Path | Auth | Description |
|---|
| GET | /integrations/github/install | Required | Get GitHub App install URL |
| GET | /integrations/github/callback | None | GitHub App install callback (internal) |
| GET | /integrations | Required | List connected provider integrations |
| DELETE | /integrations/{provider} | Required | Disconnect a provider integration |
Custom Connectors
| Method | Path | Auth | Description |
|---|
| GET | /custom-connectors | Required | List custom connector definitions |
| POST | /custom-connectors | Required | Create a custom connector definition |
| GET | /custom-connectors/{id} | Required | Get custom connector definition |
| PATCH | /custom-connectors/{id} | Required | Update custom connector definition |
| DELETE | /custom-connectors/{id} | Required | Delete custom connector definition |
| POST | /custom-connectors/{id}/discover | Required | AI-powered API discovery (infer auth, config, streams) |
| POST | /custom-connectors/{id}/generate | Required | Trigger AI code generation |
| GET | /custom-connectors/{id}/generations | Required | List generation history |
| POST | /custom-connectors/{id}/test | Required | Test custom connector in sandbox |
| POST | /custom-connectors/{id}/icon | Required | Upload connector icon (multipart form) |
Support
| Method | Path | Auth | Description |
|---|
| POST | /support/chat | Optional | Send message to support AI, receive SSE stream |