@lovable.dev/sdk 1.7.0 → 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 +158 -301
- package/dist/index.d.ts +1138 -1150
- package/dist/index.js +22 -15
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +3029 -381
- package/dist/schemas.js +716 -279
- package/dist/schemas.js.map +1 -1
- package/package.json +2 -1
- package/src/client.ts +28 -20
- package/src/generated/paths.ts +1117 -1132
- package/src/generated/version.ts +1 -1
- package/src/generated/zod/zod.gen.ts +563 -274
- package/src/index.ts +1 -0
- package/src/schemas-deprecated.ts +234 -0
- package/src/schemas.ts +11 -17
- package/src/types.ts +19 -22
package/README.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript SDK for the Lovable API.
|
|
4
4
|
|
|
5
|
-
|
|
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,350 +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
|
-
##
|
|
16
|
+
## Create an API key
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
//
|
|
51
|
-
await client.
|
|
52
|
-
const
|
|
53
|
-
console.log(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
+
## Publish a project and poll the deployment
|
|
75
82
|
|
|
76
|
-
|
|
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
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
+
Non-2xx responses throw `ApiError`. The API returns one error envelope on
|
|
113
|
+
every operation:
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
|
130
|
+
| Envelope field | On `ApiError` |
|
|
109
131
|
| --- | --- |
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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.
|
|
140
|
+
import { ApiError } from "@lovable.dev/sdk";
|
|
197
141
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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:
|
|
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
|
+
```
|
|
294
149
|
|
|
295
|
-
|
|
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`): Whether to preserve chat history
|
|
299
|
-
- `includeCustomKnowledge` (optional, default: `false`): Whether to copy custom instructions/knowledge
|
|
300
|
-
- `initialMessage` (optional): Initial chat message to send after remix completes
|
|
301
|
-
- `skipInitialRemixMessage` (optional, default: `false`): When true, suppresses the default "I've successfully remixed this project" message
|
|
302
|
-
- `skipIntegrations` (optional, default: `false`): When true, skips copying integrations to the remixed project
|
|
150
|
+
Types you will meet first:
|
|
303
151
|
|
|
304
|
-
|
|
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 |
|
|
305
162
|
|
|
306
|
-
|
|
163
|
+
## Rate limits
|
|
307
164
|
|
|
308
|
-
|
|
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).
|
|
309
170
|
|
|
310
|
-
|
|
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`.
|
|
311
175
|
|
|
312
|
-
|
|
176
|
+
```typescript
|
|
177
|
+
if (err instanceof ApiError && err.status === 429) {
|
|
178
|
+
await new Promise((resolve) => setTimeout(resolve, err.rateLimit?.retryAfterMs ?? 1000));
|
|
179
|
+
}
|
|
180
|
+
```
|
|
313
181
|
|
|
314
|
-
|
|
315
|
-
- `timeout` (optional): Maximum time to wait in ms (default: 300000 = 5 minutes)
|
|
316
|
-
- `onProgress` (optional): Callback with `(status, step?)` for progress updates
|
|
182
|
+
## Operations outside the public v1 contract
|
|
317
183
|
|
|
318
|
-
|
|
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.
|
|
319
193
|
|
|
320
|
-
##
|
|
194
|
+
## Workflows subpath
|
|
321
195
|
|
|
322
|
-
|
|
196
|
+
`@lovable.dev/sdk/workflows` defines compute-only durable workflows for the
|
|
197
|
+
Cloudflare runtime; it does not call the Lovable API.
|
|
323
198
|
|
|
324
199
|
```typescript
|
|
325
|
-
import
|
|
326
|
-
WorkspaceWithMembership,
|
|
327
|
-
ProjectResponse,
|
|
328
|
-
CreateProjectOptions,
|
|
329
|
-
MessageCompletionResult,
|
|
330
|
-
ContinuationOverride,
|
|
331
|
-
FileInput,
|
|
332
|
-
RemixProjectOptions,
|
|
333
|
-
// ... etc
|
|
334
|
-
} from "@lovable.dev/sdk";
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### `FileInput`
|
|
200
|
+
import { defineWorkflow, toWorker } from "@lovable.dev/sdk/workflows";
|
|
338
201
|
|
|
339
|
-
|
|
202
|
+
const workflow = defineWorkflow<{ message: string }, { message: string }>("normalize", async (ctx) => ({
|
|
203
|
+
message: await ctx.step.run("normalize", () => ctx.input.message.trim()),
|
|
204
|
+
}));
|
|
340
205
|
|
|
341
|
-
|
|
342
|
-
interface FileInput {
|
|
343
|
-
name: string; // Original file name (e.g., "screenshot.png")
|
|
344
|
-
data: Blob | ArrayBuffer | Uint8Array; // File contents
|
|
345
|
-
type: string; // MIME type (e.g., "image/png")
|
|
346
|
-
}
|
|
206
|
+
export default toWorker(workflow);
|
|
347
207
|
```
|
|
348
208
|
|
|
349
|
-
|
|
209
|
+
## Reference
|
|
350
210
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
- `"force"` — skip all continuation checks (force continuation)
|
|
358
|
-
- `"fresh_build"` — force a full prompt rebuild from scratch
|
|
359
|
-
- `"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 |
|