@lovable.dev/sdk 1.7.5 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  TypeScript SDK for the Lovable API.
4
4
 
5
- Stable for the public API v1 surface and versioned with semver.
5
+ > **Warning: experimental software.** This SDK is not covered by the public
6
+ > API v1 stability guarantees. Any release, including minor and patch versions,
7
+ > may change or remove methods, types, and behavior. Pin an exact version. For
8
+ > production integrations, call the public API directly.
6
9
 
7
10
  ## Installation
8
11
 
@@ -10,357 +13,205 @@ Stable for the public API v1 surface and versioned with semver.
10
13
  npm install @lovable.dev/sdk
11
14
  ```
12
15
 
13
- ## Usage
16
+ ## Create an API key
14
17
 
15
- ```typescript
16
- import { LovableClient } from "@lovable.dev/sdk";
18
+ Keys are created in the workspace settings at `https://lovable.dev/settings/api-keys`
19
+ (the **Access tokens** tab). You need the admin or owner role in the workspace.
17
20
 
18
- const client = new LovableClient({
19
- apiKey: "lov_your-api-key",
20
- });
21
+ | Setting | Effect |
22
+ | --- | --- |
23
+ | Workspace | A key belongs to one workspace and can only read or change that workspace. Resources in other workspaces return `404`. |
24
+ | Plan | The workspace must be on Business or higher; every public v1 call from a lower plan returns `402 payment_required`. Two operations (project PII labels, Security Center project inventory) require Enterprise. |
25
+ | Access | Pick Read-only or Full access per resource. Projects maps to `projects:read` / `projects:write`, Workspace maps to `workspaces:read` / `workspaces:write`. A write scope includes the matching read scope. |
21
26
 
22
- // List workspaces
23
- const workspaces = await client.listWorkspaces();
27
+ Every key from this page carries the `public:v1` audience: it reaches the
28
+ advertised public v1 management operations and nothing else. The audience is
29
+ not shown in the settings page.
24
30
 
25
- // 1. Create a project
26
- const project = await client.createProject(workspaces[0].id, {
27
- description: "Best todo app",
28
- initialMessage: "Create a todo app with authentication"
29
- });
31
+ ## Quick start
30
32
 
31
- // 2. Wait for the AI response, then get the preview URL
32
- const response = await client.waitForMessageCompletion(project.id, project.message_id);
33
- console.log(response.content); // AI's response text
34
- console.log(response.message_id); // AI message ID
35
- console.log(client.getPreviewUrl(project.id)); // Preview URL for the project
33
+ ```typescript
34
+ import { LovableClient } from "@lovable.dev/sdk";
36
35
 
37
- // 3. Send a follow-up chat message
38
- await client.chat(project.id, {
39
- message: "Add a footer",
40
- });
36
+ const client = new LovableClient({ apiKey: process.env.LOVABLE_API_KEY! });
41
37
 
42
- // 4. Send a message with file attachments
43
- import { readFile } from "fs/promises";
44
- const imageData = await readFile("design.png");
45
- await client.chat(project.id, {
46
- message: "Update the hero section to match this design",
47
- files: [{ name: "design.png", data: imageData, type: "image/png" }],
48
- });
38
+ // GET /v1/me: validates the key. A workspace-scoped key returns one workspace.
39
+ const me = await client.me();
40
+ console.log(me.email, me.workspaces?.[0]?.name);
41
+
42
+ // GET /v1/workspaces: list envelope (`data`, `pagination`) plus `workspaces`, an alias of `data`.
43
+ const { workspaces } = await client.listWorkspaces();
44
+ const workspaceId = workspaces[0].id;
49
45
 
50
- // 5. Publish the project and get the live URL
51
- await client.publish(project.id);
52
- const published = await client.waitForProjectPublished(project.id);
53
- console.log(published.url); // Live public URL
46
+ // GET /v1/projects?workspace_id=...: same envelope, plus `projects` as an alias of `data`.
47
+ const page = await client.listProjects(workspaceId, { limit: 20 });
48
+ for (const project of page.data ?? []) {
49
+ console.log(project.id, project.name, project.is_published ? project.url : "(not published)");
50
+ }
54
51
  ```
55
52
 
56
- ### Code-first workflows
53
+ Every hand-written method returns the response body unchanged, apart from the
54
+ aliases noted above. `client.typed` is an [openapi-fetch](https://openapi-ts.dev/openapi-fetch/)
55
+ client over the generated route types for any advertised operation without a
56
+ hand-written method.
57
+
58
+ ## Pagination
57
59
 
58
- Use the `workflows` subpath to define compute-only durable workflows for the Cloudflare runtime:
60
+ List operations take `limit` (1 to 100, default 50) and `cursor`, and return
61
+ `data` with `pagination.next_cursor` and `pagination.has_more`. Keep the same
62
+ filters across pages.
59
63
 
60
64
  ```typescript
61
- import { defineWorkflow, toWorker } from "@lovable.dev/sdk/workflows";
65
+ let cursor: string | undefined;
66
+ do {
67
+ const page = await client.listProjects(workspaceId, { limit: 100, cursor });
68
+ for (const project of page.data ?? []) console.log(project.id);
69
+ cursor = page.pagination.has_more ? (page.pagination.next_cursor ?? undefined) : undefined;
70
+ } while (cursor);
71
+ ```
62
72
 
63
- const workflow = defineWorkflow<{ message: string }, { message: string; runId: string }>(
64
- "project-showcase",
65
- async (ctx) => {
66
- const message = await ctx.step.run("normalize", () => ctx.input.message.trim());
67
- return { message, runId: await ctx.uuid("run") };
68
- },
69
- );
73
+ ## Create an embed URL
70
74
 
71
- export default toWorker(workflow);
72
- ```
75
+ `client.createEmbedUrl(projectId, parentOrigin)` calls
76
+ `POST /v1/projects/{project_id}/embed-url` with scope `projects:write`. It
77
+ requires Business or higher and edit permission on the project. The response
78
+ contains a preview URL valid for one hour and bound to one exact HTTPS parent
79
+ origin. Visitors can view the built preview without a Lovable login.
73
80
 
74
- Bundle the static ESM module and register it through the workflow service. Connector and internal-service access remain disabled until workflow workload identity and platform egress are available.
81
+ ## Publish a project and poll the deployment
75
82
 
76
- ### Remixing a project at a specific message
83
+ `POST /v1/projects/{project_id}/publish` (scope `projects:write`) returns `202`
84
+ with a `deployment_id`. Poll `GET /v1/projects/{project_id}/publish/{deployment_id}`
85
+ (scope `projects:read`) no more than once every 2 seconds. `completed` and
86
+ `error` are terminal; keep polling on `running` and on `unknown` (a degraded,
87
+ non-terminal state). Give up after a deadline; the example uses 10 minutes.
77
88
 
78
89
  ```typescript
79
- const client = new LovableClient({ apiKey: "lov_your-api-key" });
80
-
81
- // Remix a project at the state just before a specific message
82
- const jobId = await client.remixProject("source-project-id", {
83
- workspaceId: "target-workspace-id",
84
- messageId: "message-id-to-snapshot-at",
85
- // remixMode: "including", // use "including" to keep the message and its AI response
86
- includeHistory: true,
87
- includeCustomKnowledge: true,
90
+ const { data: accepted } = await client.typed.POST("/v1/projects/{project_id}/publish", {
91
+ params: { path: { project_id: projectId } },
92
+ body: {}, // optional: { visibility: "workspace" }
88
93
  });
94
+ if (!accepted?.deployment_id) throw new Error("publish was not accepted");
95
+
96
+ const deadline = Date.now() + 10 * 60 * 1000;
97
+ let deployment;
98
+ do {
99
+ if (Date.now() > deadline) throw new Error(`deployment ${accepted.deployment_id} did not finish in 10 minutes`);
100
+ await new Promise((resolve) => setTimeout(resolve, 2000));
101
+ ({ data: deployment } = await client.typed.GET("/v1/projects/{project_id}/publish/{deployment_id}", {
102
+ params: { path: { project_id: projectId, deployment_id: accepted.deployment_id } },
103
+ }));
104
+ } while (deployment?.status === "running" || deployment?.status === "unknown");
105
+
106
+ if (deployment?.status === "completed") console.log(deployment.url);
107
+ else console.error(deployment?.error_code, deployment?.error_message);
108
+ ```
89
109
 
90
- // Wait for the remix to complete
91
- const { projectId } = await client.waitForRemix("source-project-id", jobId, {
92
- onProgress: (status, step) => console.log(`Remix: ${status}`, step),
93
- });
110
+ ## Errors
94
111
 
95
- console.log(`Remixed project: ${projectId}`);
112
+ Non-2xx responses throw `ApiError`. The API returns one error envelope on
113
+ every operation:
96
114
 
97
- // Send a follow-up message to the remixed project
98
- const followUp = await client.chat(projectId, { message: "Add dark mode" });
99
- const response = await client.waitForMessageCompletion(projectId, followUp.message_id, {
100
- threadId: followUp.thread_id,
101
- });
115
+ ```json
116
+ {
117
+ "type": "invalid_argument",
118
+ "title": "Invalid argument",
119
+ "status": 400,
120
+ "request_id": "4bf92f3577b34da6a3ce929d0e0e4736",
121
+ "detail": "validation failed",
122
+ "errors": [{ "location": "query.limit", "message": "expected integer <= 100" }]
123
+ }
102
124
  ```
103
125
 
104
- ### Continuation override
105
-
106
- The `continuation` option on `chat()` overrides prompt cache reuse behavior. API-key auth only.
126
+ `type`, `title`, `status`, and `request_id` are always present. `detail`,
127
+ `errors[]`, and `props` are optional. Branch on `type`; `title` wording can
128
+ change.
107
129
 
108
- | Value | Effect |
130
+ | Envelope field | On `ApiError` |
109
131
  | --- | --- |
110
- | `"force"` | Skip all checks, force cache reuse |
111
- | `"fresh_build"` | Force full prompt rebuild |
112
- | `"allow_expired_cache"` | Skip expiry check only |
132
+ | `status` | `status` |
133
+ | `type` | `type` |
134
+ | `title` | `message` |
135
+ | `detail` | `detail` |
136
+ | `props` | `props` |
137
+ | `request_id`, `errors[]` | Not exposed by the SDK. Both are in the response body when you call the API directly. |
113
138
 
114
- ## API Reference
115
-
116
- HTTPie examples live in [examples/httpie.md](examples/httpie.md).
139
+ ```typescript
140
+ import { ApiError } from "@lovable.dev/sdk";
117
141
 
118
- ### `LovableClient`
142
+ try {
143
+ await client.getProject(projectId);
144
+ } catch (err) {
145
+ if (err instanceof ApiError && err.type === "project_not_found") return null;
146
+ throw err;
147
+ }
148
+ ```
119
149
 
120
- #### Constructor
150
+ Types you will meet first:
151
+
152
+ | `status` | `type` | Cause |
153
+ | --- | --- | --- |
154
+ | 401 | `unauthorized` | Missing, revoked, or expired key |
155
+ | 402 | `payment_required` | Workspace plan below Business (or Enterprise where required) |
156
+ | 403 | `insufficient_scope` | Key lacks the operation's scope |
157
+ | 403 | `forbidden` | Caller lacks the workspace or project permission |
158
+ | 403 | `audience_forbidden` | A `public:v1` key called a route outside the public v1 contract |
159
+ | 404 | `project_not_found`, `workspace_not_found` | Missing resource, or a resource in another workspace |
160
+ | 406 | `not_acceptable` | `Accept` header excludes `application/json` |
161
+ | 429 | `rate_limited` | Rate limit hit; see below |
162
+
163
+ ## Rate limits
164
+
165
+ Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and, when
166
+ known, `X-RateLimit-Reset` (Unix seconds). Limits use a sliding window: per
167
+ API key for key-authenticated requests, per user for session and OAuth
168
+ requests. Publishing is limited separately (10 per minute by default), as are
169
+ deployment status polling (120 per minute), analytics, security-center
170
+ insights, and collaborator reads (300 per minute).
171
+
172
+ On `429` the client retries the request up to three times, waiting 100 ms,
173
+ 300 ms, then 500 ms, or longer when the `Retry-After` header asks for more.
174
+ After the last retry it throws `ApiError` with `status: 429` and
175
+ `rateLimit.retryAfterMs` parsed from `Retry-After`.
121
176
 
122
177
  ```typescript
123
- new LovableClient(options: LovableClientOptions)
178
+ if (err instanceof ApiError && err.status === 429) {
179
+ await new Promise((resolve) => setTimeout(resolve, err.rateLimit?.retryAfterMs ?? 1000));
180
+ }
124
181
  ```
125
182
 
126
- - `apiKey` (required): Your Lovable API key
127
- - `baseUrl` (optional): Override the default API base URL
128
-
129
- #### Methods
130
-
131
- ##### `listWorkspaces(): Promise<WorkspaceWithMembership[]>`
132
-
133
- List all workspaces the authenticated user has access to.
134
-
135
- ##### `getWorkspace(workspaceId: string): Promise<WorkspaceWithMembership>`
136
-
137
- Get a specific workspace by ID.
138
-
139
- ##### `listProjects(workspaceId: string, options?): Promise<ProjectResponse[]>`
140
-
141
- List projects in a workspace.
142
-
143
- Options:
144
-
145
- - `limit` (optional): Maximum number of projects to return
146
- - `visibility` (optional): Filter by visibility (`"all"` | `"personal"` | `"public"` | `"workspace"`)
147
-
148
- ##### `createProject(workspaceId: string, options): Promise<ProjectResponse>`
149
-
150
- Create a new project in a workspace.
151
-
152
- Options:
153
-
154
- - `description` (required): Project description
155
- - `techStack` (optional): Technology stack (e.g., `"react"`)
156
- - `visibility` (optional): Project visibility (`"draft"` | `"private"` | `"public"`)
157
- - `templateProjectId` (optional): ID of a template project to clone
158
- - `initialMessage` (optional): Initial chat message to send to the AI agent
159
- - `files` (optional): Array of files to attach (browser `File` objects or `FileInput` objects)
160
-
161
- ##### `getProject(projectId: string): Promise<ProjectResponse>`
162
-
163
- Get a project by ID.
164
-
165
- ##### `updateProject(projectId: string, options): Promise<ProjectResponse>`
166
-
167
- Update supported project fields through `PATCH /v1/projects/{project_id}`.
168
-
169
- Options:
170
-
171
- - `display_name` (optional): Project display name
172
- - `visibility` (optional): Project visibility (`"draft"` | `"private"` | `"public"` | `"workspace_view"`)
173
-
174
- ##### `deleteProject(projectId: string): Promise<void>`
175
-
176
- Soft-delete a project through `DELETE /v1/projects/{project_id}`.
177
-
178
- ##### `chat(projectId: string, options): Promise<SendMessageResponse>`
179
-
180
- Send a chat message to a project's AI agent.
181
-
182
- Options:
183
-
184
- - `message` (required): The message to send
185
- - `files` (optional): Array of files to attach (browser `File` objects or `FileInput` objects)
186
- - `continuation` (optional): Override prompt cache continuation behavior (`"force"` | `"fresh_build"` | `"allow_expired_cache"`). API-key auth only. See [Continuation override](#continuation-override)
187
-
188
- The response includes the created `message_id` and its trajectory `thread_id`. Pass both to `waitForMessageCompletion()` to wait for the matching AI reply.
189
-
190
- ##### `listMessages(projectId: string, options?): Promise<ListMessagesResponse>`
191
-
192
- List recent project messages through `GET /v1/messages`.
193
-
194
- ##### `getMessage(projectId: string, messageId: string, options?): Promise<GetMessageResponse>`
195
-
196
- Get a message through `GET /v1/messages/{message_id}`. Pass `waitSeconds` for server-side long polling.
197
-
198
- ##### `createVariant(projectId: string, options?): Promise<CreateVariantResponse>`
199
-
200
- Create an independent variant from the project's current main branch, or from a specific full 40-character commit SHA.
201
-
202
- Options:
183
+ ## Operations outside the public v1 contract
203
184
 
204
- - `label` (optional): Display name. Defaults to the next Draft number
205
- - `baseSha` (optional): Full 40-character commit SHA to base the variant branch on
185
+ The client also exposes builder operations: project creation and remix
186
+ (`createProject`, `remixProject`), messages (`chat`, `listMessages`,
187
+ `getMessage`, `waitForMessageCompletion`, `createVariant`), database, git and
188
+ file access, uploads, knowledge, skills, connectors, folders, and
189
+ `publish()` (which calls the deprecated `POST /v1/deployments` alias). These
190
+ routes are not part of the public v1 contract. A key created in the settings
191
+ page receives `403 audience_forbidden` from all of them. They require a key
192
+ without a public audience, they carry no stability guarantee, and they are not
193
+ documented here.
206
194
 
207
- Returns:
195
+ ## Workflows subpath
208
196
 
209
- - `variant_id` (string): The variant's identifier
210
- - `label` (string): The variant's display name
211
- - `branch` (string): The git branch attached to the variant
212
- - `thread_id` (string): The trajectory thread driving the variant
213
-
214
- The API returns `403 variants_not_enabled` when variants are unavailable for the authenticated user and project workspace.
215
-
216
- ##### `waitForMessageCompletion(projectId: string, messageId: string, options?): Promise<MessageCompletionResult>`
217
-
218
- Wait for a specific message's AI response to finish. Pass the `message_id` from `chat()` or from `createProject()` with `initialMessage` (and the `thread_id` via `options.threadId` for non-main threads).
219
-
220
- Use `getPreviewUrl(projectId)` to construct the preview URL.
221
-
222
- Returns:
223
-
224
- - `status` (`"completed" | "awaiting_input" | "stopped" | "timeout" | "error"`): Terminal state
225
- - `content` (string): The AI's full response text
226
- - `message_id` (string): The AI message ID
227
- - `awaiting_input` (optional): Present when `status` is `"awaiting_input"` — a non-headless turn paused by a human-in-the-loop tool. Carries the resumable `event_id`, `prev_session_id`, and optional `input_schema`
228
- - `edit_id` / `commit_sha` / `summary` / `cost_credits` (optional): Result metadata
229
-
230
- Options:
231
-
232
- - `threadId` (optional): Trajectory thread returned by `chat()`
233
- - `timeout` (optional): Maximum total time to wait in ms (default: 600000 = 10 minutes)
234
-
235
- ##### `waitForResponse(projectId: string, options?): Promise<ChatResponse>`
236
-
237
- Deprecated compatibility helper. Prefer `waitForMessageCompletion(projectId, messageId)`.
238
-
239
- ##### `getPreviewUrl(projectId: string): string`
240
-
241
- Get the preview URL for a project. This is a synchronous method that constructs the URL from the project ID.
242
-
243
- ##### `publish(projectId: string, options?): Promise<DeploymentResponse>`
244
-
245
- Publish (deploy) a project to make it publicly accessible. The deployment runs asynchronously — use `waitForProjectPublished()` to wait for completion.
246
-
247
- Options:
248
-
249
- - `name` (optional): Custom slug for the published URL
250
-
251
- Returns:
252
-
253
- - `status` (string): Deployment status
254
- - `deployment_id` (string): The deployment ID
255
- - `url` (string): The published URL (may not be available until deployment completes)
256
-
257
- ##### `getPublishedUrl(projectId: string): Promise<string | null>`
258
-
259
- Get the published URL for a project, or `null` if not published. Fetches the latest project details to check publication status.
260
-
261
- ##### `waitForProjectReady(projectId: string, options?): Promise<ProjectResponse>`
262
-
263
- Wait for a project to reach "completed" status. Projects start in "in_progress" status while being created/built.
264
-
265
- Options:
266
-
267
- - `pollInterval` (optional): Time between polls in ms (default: 2000)
268
- - `timeout` (optional): Maximum time to wait in ms (default: 300000 = 5 minutes)
269
- - `onProgress` (optional): Callback for status updates
270
-
271
- Throws an error if the project fails or timeout is reached.
272
-
273
- ##### `waitForProjectPublished(projectId: string, options?): Promise<ProjectResponse>`
274
-
275
- Wait for a project to be published (deployed) and have a live URL.
276
-
277
- Options:
278
-
279
- - `pollInterval` (optional): Time between polls in ms (default: 3000)
280
- - `timeout` (optional): Maximum time to wait in ms (default: 600000 = 10 minutes)
281
- - `onProgress` (optional): Callback for status updates
282
-
283
- Throws an error if timeout is reached.
284
-
285
- ##### `remixProject(sourceProjectId: string, options): Promise<string>`
286
-
287
- Remix (fork) an existing project, optionally at a specific message point in time.
288
-
289
- When `messageId` is provided, the remix captures the project state as it was just before that message was processed (default `remixMode: "before"`). Set `remixMode: "including"` to include the message and its AI response in the remix. Without `messageId`, the full current state is remixed.
290
-
291
- Returns the remix job ID for polling with `waitForRemix()`.
292
-
293
- Options:
294
-
295
- - `workspaceId` (required): Target workspace for the new project
296
- - `messageId` (optional): Message ID to snapshot at — by default the remix reflects the project state just before this message
297
- - `remixMode` (optional): `"before"` (default) captures state before the message; `"including"` captures state after the message and its AI response
298
- - `includeHistory` (optional, default: `false`): Copy chat history. Requires edit access to the original project. View-only access or a public remix link is not enough.
299
- - `includeFiles` (optional, default: `false`): Copy project Files. Requires edit access to the original project. View-only access or a public remix link is not enough.
300
- - `includeCustomKnowledge` (optional, default: `false`): Whether to copy custom instructions/knowledge
301
- - `initialMessage` (optional): Initial chat message to send after remix completes
302
- - `skipInitialRemixMessage` (optional, default: `false`): When true, suppresses the default "I've successfully remixed this project" message
303
- - `skipIntegrations` (optional, default: `false`): When true, skips copying integrations to the remixed project
304
-
305
- Omitting `includeFiles` or setting it to `false` skips Files for everyone. Set it to `true` to copy Files; without edit access to the original project, the request returns `403 insufficient_permissions`.
306
-
307
- Existing callers that relied on automatic Files copying must now send `includeFiles: true`. Chat history stays independent.
308
-
309
- This option does not control attachments in copied chat history or app code and assets.
310
-
311
- ##### `waitForRemix(sourceProjectId: string, jobId: string, options?): Promise<RemixResult>`
312
-
313
- Wait for a remix operation to complete. Polls `GET /v1/remix-jobs/{job_id}` until the job finishes. The `sourceProjectId` argument is kept for SDK compatibility.
314
-
315
- Returns:
316
-
317
- - `projectId` (string): The ID of the newly created project
318
-
319
- Options:
320
-
321
- - `pollInterval` (optional): Time between polls in ms (default: 2000)
322
- - `timeout` (optional): Maximum time to wait in ms (default: 300000 = 5 minutes)
323
- - `onProgress` (optional): Callback with `(status, step?)` for progress updates
324
-
325
- Throws an error if the remix fails or timeout is reached.
326
-
327
- ## Types
328
-
329
- The SDK exports TypeScript types for all API responses. See `src/types.ts` for the full list.
197
+ `@lovable.dev/sdk/workflows` defines compute-only durable workflows for the
198
+ Cloudflare runtime; it does not call the Lovable API.
330
199
 
331
200
  ```typescript
332
- import type {
333
- WorkspaceWithMembership,
334
- ProjectResponse,
335
- CreateProjectOptions,
336
- MessageCompletionResult,
337
- ContinuationOverride,
338
- FileInput,
339
- RemixProjectOptions,
340
- // ... etc
341
- } from "@lovable.dev/sdk";
342
- ```
343
-
344
- ### `FileInput`
201
+ import { defineWorkflow, toWorker } from "@lovable.dev/sdk/workflows";
345
202
 
346
- For Node.js or non-browser environments, use `FileInput` instead of the browser `File` API:
203
+ const workflow = defineWorkflow<{ message: string }, { message: string }>("normalize", async (ctx) => ({
204
+ message: await ctx.step.run("normalize", () => ctx.input.message.trim()),
205
+ }));
347
206
 
348
- ```typescript
349
- interface FileInput {
350
- name: string; // Original file name (e.g., "screenshot.png")
351
- data: Blob | ArrayBuffer | Uint8Array; // File contents
352
- type: string; // MIME type (e.g., "image/png")
353
- }
207
+ export default toWorker(workflow);
354
208
  ```
355
209
 
356
- ### `ContinuationOverride`
210
+ ## Reference
357
211
 
358
- Controls prompt cache continuation behavior for a message:
359
-
360
- ```typescript
361
- type ContinuationOverride = "force" | "fresh_build" | "allow_expired_cache";
362
- ```
363
-
364
- - `"force"` — skip all continuation checks (force continuation)
365
- - `"fresh_build"` — force a full prompt rebuild from scratch
366
- - `"allow_expired_cache"` — skip cache expiry check but respect other continuation checks
212
+ | Document | Content |
213
+ | --- | --- |
214
+ | [API_SPEC.md](API_SPEC.md) | Every advertised public v1 operation with its scope, parameters, and response schema |
215
+ | https://api.lovable.dev/v1/openapi.json | The served OpenAPI document; authoritative when the two disagree |
216
+ | [examples/httpie.md](examples/httpie.md) | The same calls over plain HTTP |
217
+ | [src/types.ts](src/types.ts) | Exported request and response types |