@lovable.dev/sdk 1.7.5 → 1.9.0

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,204 @@ 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 |
113
-
114
- ## API Reference
115
-
116
- HTTPie examples live in [examples/httpie.md](examples/httpie.md).
117
-
118
- ### `LovableClient`
119
-
120
- #### Constructor
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. |
121
138
 
122
139
  ```typescript
123
- new LovableClient(options: LovableClientOptions)
124
- ```
125
-
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>`
140
+ import { ApiError } from "@lovable.dev/sdk";
199
141
 
200
- Create an independent variant from the project's current main branch, or from a specific full 40-character commit SHA.
201
-
202
- Options:
203
-
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
206
-
207
- Returns:
208
-
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.
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
+ ```
308
149
 
309
- This option does not control attachments in copied chat history or app code and assets.
150
+ Types you will meet first:
310
151
 
311
- ##### `waitForRemix(sourceProjectId: string, jobId: string, options?): Promise<RemixResult>`
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 |
312
162
 
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.
163
+ ## Rate limits
314
164
 
315
- Returns:
165
+ Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and, when
166
+ known, `X-RateLimit-Reset` (Unix seconds). Limits are per user across all of
167
+ that user's keys and sessions, in a sliding window. Publishing is limited
168
+ separately (10 per minute by default) and so is deployment status polling
169
+ (120 per minute).
316
170
 
317
- - `projectId` (string): The ID of the newly created project
171
+ On `429` the client retries the request up to three times, waiting 100 ms,
172
+ 300 ms, then 500 ms, or longer when the `Retry-After` header asks for more.
173
+ After the last retry it throws `ApiError` with `status: 429` and
174
+ `rateLimit.retryAfterMs` parsed from `Retry-After`.
318
175
 
319
- Options:
176
+ ```typescript
177
+ if (err instanceof ApiError && err.status === 429) {
178
+ await new Promise((resolve) => setTimeout(resolve, err.rateLimit?.retryAfterMs ?? 1000));
179
+ }
180
+ ```
320
181
 
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
182
+ ## Operations outside the public v1 contract
324
183
 
325
- Throws an error if the remix fails or timeout is reached.
184
+ The client also exposes builder operations: project creation and remix
185
+ (`createProject`, `remixProject`), messages (`chat`, `listMessages`,
186
+ `getMessage`, `waitForMessageCompletion`, `createVariant`), database, git and
187
+ file access, uploads, knowledge, skills, connectors, folders, and
188
+ `publish()` (which calls the deprecated `POST /v1/deployments` alias). These
189
+ routes are not part of the public v1 contract. A key created in the settings
190
+ page receives `403 audience_forbidden` from all of them. They require a key
191
+ without a public audience, they carry no stability guarantee, and they are not
192
+ documented here.
326
193
 
327
- ## Types
194
+ ## Workflows subpath
328
195
 
329
- The SDK exports TypeScript types for all API responses. See `src/types.ts` for the full list.
196
+ `@lovable.dev/sdk/workflows` defines compute-only durable workflows for the
197
+ Cloudflare runtime; it does not call the Lovable API.
330
198
 
331
199
  ```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`
200
+ import { defineWorkflow, toWorker } from "@lovable.dev/sdk/workflows";
345
201
 
346
- For Node.js or non-browser environments, use `FileInput` instead of the browser `File` API:
202
+ const workflow = defineWorkflow<{ message: string }, { message: string }>("normalize", async (ctx) => ({
203
+ message: await ctx.step.run("normalize", () => ctx.input.message.trim()),
204
+ }));
347
205
 
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
- }
206
+ export default toWorker(workflow);
354
207
  ```
355
208
 
356
- ### `ContinuationOverride`
209
+ ## Reference
357
210
 
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
211
+ | Document | Content |
212
+ | --- | --- |
213
+ | [API_SPEC.md](API_SPEC.md) | Every advertised public v1 operation with its scope, parameters, and response schema |
214
+ | https://api.lovable.dev/v1/openapi.json | The served OpenAPI document; authoritative when the two disagree |
215
+ | [examples/httpie.md](examples/httpie.md) | The same calls over plain HTTP |
216
+ | [src/types.ts](src/types.ts) | Exported request and response types |