# Lovelio API reference

Recruitment automation API. Create jobs, manage candidates, run ad-hoc actions, integrate with your tools.

Generated from the OpenAPI 3.1 document at https://lovelio.ai/api/v1/openapi.json.
API version 7.2.0. 157 paths, 234 operations.

This is the plain-markdown copy of the reference rendered at https://lovelio.ai/docs/api.

## Fetch less than all of this

| URL | What you get |
| --- | --- |
| `https://lovelio.ai/docs/api.md` | Everything: this header plus every operation in full. |
| `https://lovelio.ai/docs/api.md?index=1` | This header plus the endpoint index. No per-operation detail. |
| `https://lovelio.ai/docs/api.md?tag=Jobs` | This header plus one section in full. Comma-separate for several: `?tag=Jobs,Applications`. |
| `https://lovelio.ai/api/v1/openapi.json` | The machine-readable source this file is generated from. |

Tags: System, Accounts, Jobs, Job Ads, Candidates, Candidate Imports, Applications, Interviews, Submissions, Placements, Quotas, Marketplace, Webhooks, Batch, Activities, Documents, Tasks, Outreach, Talent Pools, Review Queue, Forms, Referees, Integrations, Chat Integrations, Analytics, Calendar, Scheduled Emails, Workflow Rules, Email Templates, Stages, Distribution, Clients, Specs, Search, Business Development.

## Base URL

Docs live on lovelio.ai. The API does not. Every workspace belongs to a region and an API key only works on its own region's host. Calling `lovelio.ai/api/v1/...` returns a JSON error naming all three hosts rather than data.

| Region | Base URL |
| --- | --- |
| Production (US (N. California)) | `https://us.lovelio.ai/api/v1` |
| Production (EU (London)) | `https://eu.lovelio.ai/api/v1` |
| Production (ANZ (Sydney)) | `https://anz.lovelio.ai/api/v1` |

A workspace's exact host is shown in Settings > API keys.

## Authentication

```http
Authorization: Bearer sk_live_...
```

API key, sent as Authorization: Bearer <key>. Three key types: sk_live_ (production), sk_test_ (development - same workspace, same data, marked as a test key; use a separate workspace if you need isolated test data), and sk_trial_ (issued at signup, expires 7 days later - swap to a live key from Settings > API keys). Keys are server-generated, shown once at creation, and scoped per permission (or admin for everything). Lovelio Connect apps authenticate with the OAuth access token from the connect flow instead (Authorization: Bearer lc_at_...) - it carries exactly the scopes the agency approved, enforced on every request. Money is gated at FIELD level, not route level: the three scopes placements:financials:read, clients:financials:read and marketplace:financials:read unlock salary, fee, contract rates, expected GP, commission percents, the client fee schedule and split-fee deal amounts. Without them those fields return null and everything else on the record still comes through - a read never 403s for want of a money scope. admin satisfies all three. Requesting any of them puts a Connect app in the elevated review tier. See /docs/agents/build-an-integration.

## Pagination and shared parameters

Every list endpoint takes these. Cursor-based: read `meta.next_cursor` from a response and pass it back as `after`.

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `limit` | query | integer, 1-100, default 25 | Rows per page. Clamped to the 1-100 range rather than rejected, so asking for 500 returns 100 - read meta.has_more and page with the cursor. Unparseable values fall back to 25. |
| `after` | query | string | Cursor for forward pagination |
| `before` | query | string | Cursor for backward pagination |
| `created_after` | query | string (date-time) | Only rows created at or after this ISO 8601 date or datetime (inclusive). Malformed values are a 422, never silently ignored. |
| `created_before` | query | string (date-time) | Only rows created at or before this ISO 8601 date or datetime (inclusive). Malformed values are a 422, never silently ignored. |

## Rate limit headers

| Header | Type | Description |
| --- | --- | --- |
| `X-RateLimit-Limit` | integer | Requests allowed per minute |
| `X-RateLimit-Remaining` | integer | Requests remaining in window |
| `X-RateLimit-Reset` | integer | Unix timestamp when window resets |

## Guides

- https://lovelio.ai/docs/agents - what an agent can drive end to end
- https://lovelio.ai/docs/agents/top-10-tasks - runnable examples for the ten most common calls
- https://lovelio.ai/docs/agents/build-an-integration - sandbox agency, test API key, live webhook testing
- https://lovelio.ai/docs/agents/error-handling - error envelope, request IDs, retries, idempotency keys
- https://lovelio.ai/docs/agents/webhooks - subscribing and verifying HMAC signatures
- https://lovelio.ai/docs/agents/sdks - single-file TypeScript and Python clients
- https://lovelio.ai/docs/api/changelog - every version, including breaking changes
- https://lovelio.ai/llms-full.txt - all of the above inlined in one fetch

## Endpoint index

Filtered to: Quotas.

### Quotas

Quota plan, periods and the recognition board: credit-share money against targets for the agency, teams and consultants

- `GET /quotas/plan` - Get quota plan
- `GET /quotas/periods` - List quota periods
- `GET /quotas/periods/{id}` - Get quota period board

---

## Quotas

Quota plan, periods and the recognition board: credit-share money against targets for the agency, teams and consultants

### GET /quotas/plan

**Get quota plan**

The agency's quota plan. Auto-created with defaults on first read, so this never 404s. Read-only: plan defaults, periods and targets are managed in Settings > Quotas. Requires quotas:read.

Operation ID: `getQuotaPlan`

**Responses**

| Code | Description | Body |
| --- | --- | --- |
| `200` | The quota plan | { success, data: QuotaPlan } |

### GET /quotas/periods

**List quota periods**

Quota periods, newest first. Periods auto-roll on read, so the current one always exists. No cursor: an agency mints at most 12 periods a year, so limit is the whole story. Requires quotas:read.

Operation ID: `listQuotaPeriods`

**Query parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer, 1-100, default 12 | no |  |

**Responses**

| Code | Description | Body |
| --- | --- | --- |
| `200` | Quota periods | { success, data: array of QuotaPeriod, meta } |

### GET /quotas/periods/{id}

**Get quota period board**

One period's quota board: recognised credit-share money, placement counts and targets for the agency, each team and each consultant, plus the period's adjustments ledger. The figures are the quota recognition engine's own answer - recognition basis, credit splits, adjustments and locked-period snapshots all respected - so they always match the Placements dashboard and the Booked card. Money is one line per currency, never converted. The board is pruned to the API key's data scope before it is returned: an all-scope key gets the whole board; a team-scoped key gets its own teams and people, never the agency row or other desks. Pass a qpd_ id, or the literal `current` for the period containing today. Requires quotas:read.

Operation ID: `getQuotaPeriodBoard`

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | qpd_ id, or `current`. |

**Responses**

| Code | Description | Body |
| --- | --- | --- |
| `200` | The period's quota board | { success, data: QuotaPeriod } |

## Schemas

Objects referenced by the operations above.

### Meta

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `request_id` | string | no |  |
| `count` | integer | no |  |
| `has_more` | boolean | no |  |
| `next_cursor` | string \| null | no |  |
| `prev_cursor` | string \| null | no |  |

### QuotaPeriod

One quota period - a month, quarter or year in the agency's own timezone, auto-created as time passes. Locking a period freezes its figures; later changes to counted placements post as adjustments to the current open period.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | no |  |
| `label` | string | no |  |
| `start_date` | string (date) | no | Inclusive. |
| `end_date` | string (date) | no | Inclusive. |
| `status` | string (`open`, `published`, `locked`) | no |  |
| `agency_target` | number \| null | no | The agency-wide target. Null when none is set (targets are never required) AND null for any key whose data scope is not the whole agency - it is an agency-wide disclosure and travels with the agency block. |
| `locked_at` | string (date-time) \| null | no |  |

### QuotaPlan

The agency's quota plan - one per agency, auto-created with defaults on first read (zero setup). Read-only over the API; plan defaults are managed in Settings > Quotas.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `cadence` | string (`monthly`, `quarterly`, `yearly`) | no |  |
| `currency` | string | no | ISO 4217 code targets are measured in. One currency per plan; other currencies are reported as their own lines, never converted. |
| `recognition_basis` | string (`offer_accepted`, `start_date`) | no | When a placement counts: when the offer was accepted, or when the candidate starts (default). |
| `include_permanent` | boolean | no |  |
| `include_contract` | boolean | no | Contract placements count their expected GP; perm placements count the fee. |
| `extensions_rule` | string (`extension_start`, `not_counted`) | no |  |
| `quarter_start_month` | integer, 1-12 | no | First month of the cycle. 1 = Jan/Apr/Jul/Oct quarters; 7 = an Australian financial year. |
| `lock_closed_periods` | boolean | no |  |
