← Help centre

V1 API and webhooks for developers

Signed in? Ask Lovelio this question inside the app - it answers from this same page.

Lovelio has a REST API for jobs, candidates, applications, clients, submissions, placements, interviews, talent pools, emails, search, and more. The API is served on your region's app domain - the base URL is https://us.lovelio.ai/api/v1 (your workspace's region, the domain you sign in on) and API keys only work in their own region. MCP tools for AI agents are available at https://us.lovelio.ai/api/mcp. The API reference is generated from the OpenAPI spec.

MCP exposes native phone-reference creation and answer submission, reusable forms and questions, candidate import management, and webhook maintenance. Import tools create upload slots; the client must upload the file bytes and check processing results. Discovery lists the tools the connection can request, while record permissions, action roles and account settings still apply. Consultant OAuth connections have a stricter scope ceiling than admin connections, including no email or form scopes. AI stage recommendations require a human decision. Use a supervised rehearsal before relying on a bot team for the whole agency workflow.

REST errors include a public help link and matching request IDs. Unknown paths return JSON 404; unsupported methods return JSON 405 with Allow. Browser integrations call their own backend and keep credentials on that server. MCP uses its OAuth consent flow for reads and writes.

Successful MCP tools return the API record in structuredContent.data, including tools that dispatch through a batch action. Use those IDs and fields for the next step even when the text only says the action succeeded.

MCP candidate search returns ranked results, search notes and verification limits. Both search tools accept a page number. Keep the query and limit unchanged when requesting the next page. Both candidate search API doors count every returned candidate against the agent access rules, including candidates shown only because a CV mentions an employer.

Referees added by an agent appear on the candidate record and in list_referees. The returned referee ID works with send_reference_request. A referee must belong to the candidate on that application. Reference forms still require forms:write, and reviewing a completed reference requires the job owner while the job is open.

Where the docs live

  • Docs hub: lovelio.ai/docs
  • API reference: lovelio.ai/docs/api (rendered live from the machine-readable spec at /api/v1/openapi.json)
  • API reference as plain markdown, for an AI agent or anything that does not run JavaScript: lovelio.ai/docs/api.md. Add ?index=1 for the endpoint list alone or ?tag=Jobs for one section. The interactive page includes a server-rendered endpoint index; the markdown version includes the full contract.
  • Every doc in one fetch: lovelio.ai/llms-full.txt (the index alone is lovelio.ai/llms.txt)
  • Agent and developer guides: lovelio.ai/docs/agents (quickstart, top 10 tasks, error handling, webhooks, reference checks, MCP quickstart, single-file TypeScript and Python SDK clients)
  • API changelog: lovelio.ai/docs/api/changelog

API keys and scopes

  • Keys are created in Settings under the "API" tab (/settings/api-keys). The full key is shown once at creation and cannot be retrieved again - only a prefix is stored for display. Revoking a key takes effect immediately.
  • Keys created in Settings start with sk_live_. Signing up via the API (POST /api/v1/accounts/signup) returns a trial key starting sk_trial_ with the admin scope, active immediately.
  • Every request sends the key as a Bearer token in the Authorization header.
  • Keys carry scopes per resource. Every operation in the generated reference names the exact scope its handler enforces; for example, candidate activity reads use activities:read. Adding a note or logging a call on one record (POST /api/v1/activities, or the add_note and log_call batch actions) is covered by EITHER activities:write OR that record's own write scope, so a key with placements:write can note a placement. The same rule covers documents: uploading, deleting, listing or downloading a file on one record (the /api/v1/documents routes, or the list_documents, delete_document and set_candidate_cv batch actions) is covered by documents:read or documents:write OR that record's own read or write scope, and listing activity for one record type is covered by activities:read OR that record's read scope. The "admin" scope grants everything and is the default if no scopes are chosen. A request missing a required scope gets a 403 INSUFFICIENT_SCOPE error that says in plain English what the call needs, what the key holds, and where to add it (the developer portal's Access panel for a partner app, Settings under API for an agency key).
  • Money has its own three scopes, and they gate FIELDS rather than routes: placements:financials:read (salary, fee, contract charge and pay rates, expected GP, and the percent on each credit), clients:financials:read (the client fee schedule - what the agency charges), and marketplace:financials:read (split-fee deal amounts and shares). A key without them still reads placements, clients and deals normally and still gets a 200 - those specific fields just come back null. It is never a 403. The admin scope satisfies all three.
  • MCP also accepts a dedicated scoped API key in its Authorization header when a client cannot complete OAuth. Company keys have agency-wide record access and no separate consent ceiling. Enter secrets only in secure client settings. Key revocation and scope reductions apply on the next call.
  • Personal MCP OAuth supports every explicit V1 scope, including accounts:write, subject to admin rules and fresh consent. Actions use the connected person's current role and record access. Person-bound and app-install credentials cannot create API keys. Connect partner apps still cannot request accounts:write or admin.
  • Partner-app traffic (keys starting lc_at_) is counted per resource so the connecting agency can see what an app has actually used. Ordinary tenant API keys are never counted this way - that is the agency reading their own data.
  • Why they are separate: a job board needs to know a placement happened, an invoicing tool needs the fee, and they are not the same app. Asking for a money scope puts a Lovelio Connect app in the elevated review tier, which means a human reviews it and it starts with a low cap on connections.

Request and response basics

  • Every response uses one envelope: success, data, meta (with a request_id), and error. Error objects carry a stable code (for example VALIDATION_ERROR, RESOURCE_NOT_FOUND, RATE_LIMIT_EXCEEDED), a message, sometimes a field, and a docs link. Quote the request_id when reporting a problem.
  • Ids are typed: every record id carries a prefix that says what it is, for example job_, cnd_ (candidate), app_ (application), cli_ (client), pla_ (placement), sub_ (submission), ivw_ (interview), tsk_ (task), whk_ (webhook), pool_ (talent pool).
  • Collections that can grow use cursor pagination: pass limit (default 25, maximum 100) and after with the opaque next_cursor from the previous page. Responses include count, has_more, and next_cursor. Bounded configuration and catalogue lists return the full collection and say so on their operation. Date-filtered lists document created_after and created_before where supported; a malformed date returns an error rather than being ignored.

Idempotency

POST operations marked idempotent in the generated reference require an Idempotency-Key header (any unique string, a UUID works). Lovelio claims the account-scoped key before the write and binds it to the method, path, query, content, credential and current permissions for 24 hours. JSON objects compare by content. Uploads include every form field, record id, filename, content type and file byte hash; random multipart boundaries are ignored. An identical completed retry replays the saved response with X-Idempotency-Replayed. A concurrent retry returns IDEMPOTENCY_IN_PROGRESS with Retry-After. A changed request or authorization context returns IDEMPOTENCY_KEY_REUSED. Reconcile the original operation before creating another key. An uncertain outcome returns IDEMPOTENCY_OUTCOME_UNKNOWN and its X-Idempotency-Operation-ID; reconcile the resource before creating another key.

Completed results and unstarted claims expire after 24 hours. Interrupted or uncertain executions stay reserved beyond that window; waiting for expiry never makes an uncertain write safe to repeat. Legacy receipts without an authorization-bound fingerprint fail closed and require reconciliation.

Async work: 202 + task

Operations that take real time (creating a job from a description, parsing a CV, running an assessment, publishing an ad, enrichment) return 202 Accepted with a task_id, an estimated_seconds hint (typically 20), and a poll_url. Poll GET /api/v1/tasks/{task_id} until status is completed or failed. Task status moves queued, processing, then completed or failed; job creation tasks also expose per-step progress (description, criteria, questions, compensation, ad, social).

Batch actions

POST /api/v1/batch runs up to 100 operations in one request. Each operation names an action, its payload and a required per-operation idempotency_key. Lovelio atomically claims each key and binds it to that action, payload, credential and current permissions for 24 hours. Operations run independently: one failure does not stop the rest, and each is scope-checked. Read every per-operation status and error code before treating the batch as successful.

Recruiter Flows

  • Assessments: POST /applications/{id}/assessment queues the same fresh Client-DNA-aware assessment as the product. Application reads include the score, recommendation, confidence score, confidence factors, and phone_screen_score.
  • Screens: POST /form-instances/screening prepares or regenerates the candidate-aware screen. POST /form-instances/{id}/submit saves or completes its answers through the same scoring and stage path as the product.
  • Interviews: GET /interviews/{id}/context returns the prep digest, attendees, per-interviewer scorecards, consolidated verdict, and next actions. POST /interviews/{id}/scorecard writes the real per-interviewer form. An outcome never moves the stage.
  • References: candidate referees may have email or phone details. A submitted referee contact is not a completed report. The complete partner recipe at /docs/agents/reference-checks reads those contacts, tracks the external check, attaches the completed report to Documents and records native candidate Timeline activity.
  • Client DNA: GET /clients/{id}/dna returns the client's traits and notes; POST adds a refinement note through the shared action and schedules reanalysis.

Rate limits

  • 600 requests per minute on an active paid plan; 60 per minute on trial, past-due, or cancelled plans.
  • Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Going over returns 429 with a Retry-After header.
  • Some actions have extra caps, for example webhook subscription creation is limited to 10 per hour per key.

Webhooks

  • Subscribe with POST /api/v1/webhooks giving a URL and a list of events, or ["*"] for everything. The signing secret (whsec_ prefix) is returned once at creation - store it. Rotate it any time via the rotate-secret endpoint; delete a subscription with DELETE.
  • The event catalogue covers the real lifecycle: applications received, scored, stage or status changed; jobs created; job ads published; interviews scheduled, cancelled, outcome recorded; submissions created and responded to; placements created and guarantee status changed; specs (floats) sent, viewed, responded, converted; emails sent, scheduled, cancelled; references received; enrichment finished; and marketplace intro and deal events. Fetch the current list programmatically from GET /api/v1/webhooks/events. Every listed event is genuinely emitted; there are no offer.* events - placements replaced offers.
  • Every delivery carries X-Lovelio-Event, X-Lovelio-Timestamp, and X-Lovelio-Signature headers. The signature is HMAC-SHA256 of "timestamp.body" using the subscription secret, hex encoded with a sha256= prefix. Verify it and reject timestamps older than 5 minutes.
  • Delivery is at-least-once: deduplicate on the event_id in the payload. A timely 2xx response counts as delivered; failures retry with backoff and eventually become failed. Read the current OpenAPI operation for the delivery policy instead of hard-coding an attempt count. Past deliveries are listed per subscription and a specific delivery can be replayed via the API.

Limits and edge cases

  • A revoked key fails immediately with an authentication error. An expired trial returns a 403 TRIAL_EXPIRED error.
  • Batch requests over 100 operations are rejected whole with BATCH_TOO_LARGE.
  • Webhook subscriptions naming an event that does not exist are rejected at creation.
  • Walk-in demo accounts never deliver webhooks.

Common questions

  • Where do I get an API key? Settings, "API" tab. Copy the key when it is shown - it is displayed once.
  • My key stopped working. Check it was not revoked in Settings, and if it starts with sk_trial_, check whether your trial has ended.
  • I got a 429. You hit the per-minute limit; wait for the Retry-After seconds and retry. Paid plans get 600 requests per minute.
  • How do I avoid duplicate records on retries? Send one Idempotency-Key per logical POST. Retry only with that same key until Lovelio returns a saved response.
  • Should I poll or use webhooks? Prefer webhooks for events they cover; they avoid wasteful polling.