TL;DR: Two ways to reach a backend. The JavaScript SDK (loaded from https://lingcode.dev/sdk/lingcode-v1.js) is the primary interface — data, auth, storage, functions, vector, push, telemetry, and config all hang off one client. Under it is a plain REST API at https://lingcode.dev/api/cloud/be/<backend-id>/… that the SDK calls — documented here for non-JS clients.
This page is the flat reference. For the why and the worked examples, follow the per-product guides — each is linked from its section here. Every signature below is taken from the shipped SDK type definitions; every endpoint from the live routes.
The SDK is a single zero-dependency file. Load it and create a client with your backend's data URL and public anon key (both from lingcode.dev/backends → your backend). Inside /try and the Mac preview the client is pre-injected as window.lingcode.
<script src="https://lingcode.dev/sdk/lingcode-v1.js"></script>
<script>
const lingcode = LingCode.createClient(url, anonKey, options?);
</script>
url: string — the backend data URL, https://lingcode.dev/api/cloud/be/<backend-id>.anonKey: string — the public anon key (safe to ship in the browser).options.detectSessionInUrl?: boolean — default true; set false to skip reading ?lc_session/?lc_magic from the URL on construct.Returns a LingCodeClient:
| Member | Type | Notes |
|---|---|---|
url | string | The backend data URL. |
anonKey | string | The anon key in use. |
backendId | string | The backend id parsed from the URL. |
ready | Promise<LingCodeClient> | Resolves after any auth redirect in the URL is consumed. await it once on load. |
from(table) | QueryBuilder | Start a data query. |
auth · storage · functions · vector · push · telemetry · config | namespaces | See below. |
Every async data/auth/storage call resolves to { data, error } — check error before using data. On failure, error is a LingCodeError with code: string | null (a server code like "where_required" or "object_not_found") and status: number (the HTTP status, 0 for client-side errors).
from(table) → QueryBuilderChain filters and modifiers, then a terminal op. update() and delete() require a filter. Full guide: Database.
| Method | Signature | Notes |
|---|---|---|
| eq · neq · gt · gte · lt · lte | (column, value) => this | Comparison filters; multiple AND together. |
| like · ilike | (column, value: string) => this | Substring match; ilike is case-insensitive. |
| in | (column, values: any[]) => this | Membership. |
| is | (column, null | "not_null") => this | IS NULL / IS NOT NULL. |
| match | (filters: Record<string,any>) => this | Several equality filters at once. |
| order | (column, { ascending? }) => this | Sort. |
| limit | (n: number) => this | Cap rows (≤ 200 per page). |
| range | (from: number, to: number) => this | Paginate by index. |
| select | () => Promise<Result<T[]>> | Read. |
| insert | (row | row[]) => Promise<Result<T[]>> | Create. |
| update | (patch) => Promise<Result<T[]>> | Requires a filter. |
| delete | () => Promise<Result<T[]>> | Requires a filter. |
| subscribe | (onChange, onError?) => Unsubscribe | Live INSERT/UPDATE/DELETE, RLS-filtered. See Realtime. |
client.authFull guide: Authentication. Session = { user: { id, email } | null, token }.
| Method | Signature |
|---|---|
| signUp | ({ email, password }) => Promise<Result<Session>> |
| signIn · signInWithPassword | ({ email, password }) => Promise<Result<Session>> |
| signInWithOAuth | (provider: "google"|"github"|"apple"|string, { redirectTo? }) => void — top-level navigation; session auto-stored on return. |
| getProviders | () => Promise<Record<string, { available, source? }>> |
| sendMagicLink | ({ email, redirectTo? }) => Promise<Result<{ sent }>> |
| verifyMagicLink | (token) => Promise<Result<Session>> |
| sendOtp | ({ email }) => Promise<Result<{ sent }>> |
| verifyOtp | ({ email, code }) => Promise<Result<Session>> |
| getUser | () => User | null |
| getToken | () => string | null |
| lastError | () => string | null — OAuth error code from the last redirect. |
| signOut | () => Promise<{ error: null }> |
client.storage.from(bucket)Bucket is "public" or "private". Full guide: Storage.
| Method | Signature |
|---|---|
| upload | (path, file: Blob|File|ArrayBuffer|string, { contentType? }) => Promise<Result<{ bucket, path, bytes, url }>> |
| download | (path) => Promise<Result<Blob>> |
| getPublicUrl | (path) => string |
| remove | (path) => Promise<Result<{ removed }>> |
client.functionsFull guide: Functions.
| Method | Signature |
|---|---|
| invoke | <T>(slug: string, body?: any) => Promise<Result<T>> |
client.vectorFull guide: Vector search.
| Method | Signature |
|---|---|
| search | ({ table, column, embedding: number[], limit?, metric?: "cosine"|"l2"|"ip" }) => Promise<Result<T[]>> |
| embed | (input: string | string[]) => Promise<Result<{ embedding, embeddings, model, dimensions }>> |
client.push| Method | Signature |
|---|---|
| isSupported | () => boolean |
| subscribe | ({ serviceWorker? }) => Promise<Result<any>> — registers the service worker + PushManager subscription, then stores it. |
client.telemetry| Method | Signature |
|---|---|
| logEvent | (name, params?: Record<string, string|number|boolean>) => void |
| logScreen | (name) => void |
| trace | (name, ms: number) => void |
| recordError | (err: Error | { message?, stack? } | string) => void |
| setUserId | (id: string | null) => void |
| setUserProperties | (props) => void |
| flush | () => Promise<Result> |
client.configRemote config + experiment variants.
| Member | Signature |
|---|---|
| ready | Promise<ConfigApi> — resolves once config has loaded. |
| get | <T>(key, def?: T) => T |
| all | () => Record<string, any> |
The SDK calls these. Use them directly from any non-JS client. Base URL is https://lingcode.dev/api/cloud/be/<backend-id> for the app-facing data plane.
Send the anon key as Authorization: Bearer <anon-key> (or ?apikey=<anon-key>) for anonymous, RLS-public access. After a user signs in, send their session token as Authorization: Bearer <user-token> so requests run as that user. The MFA, token-refresh, and sign-out endpoints require the user token.
| Method | Path | Wraps |
|---|---|---|
| POST | /select | from().select() |
| POST | /insert | from().insert() |
| POST | /update | from().update() (filter required) |
| POST | /delete | from().delete() (filter required) |
Bodies carry the table, filters, and row/patch. The runtime data plane is table-CRUD — no JOINs, no upserts, ≤ 200 rows per page (see Database).
| Method | Path | Body / notes |
|---|---|---|
| POST | /auth/signup | { email, password } |
| POST | /auth/signin | { email, password } |
| POST | /auth/magiclink/request | { email, redirect_url } |
| POST | /auth/magiclink/verify | { token } |
| POST | /auth/otp/request | { email } |
| POST | /auth/otp/verify | { email, code } |
| POST | /auth/apple/native | { identity_token } — native Apple Sign In |
| POST | /auth/token/refresh | { refresh_token } |
| POST | /auth/signout | { refresh_token } or { all: true } · user token |
| POST | /auth/mfa/enroll | → { secret, qr_code } · user token |
| POST | /auth/mfa/verify | { code } → session at AAL2 · user token |
| POST | /auth/mfa/challenge | user token |
| GET | /auth/mfa/factors | user token |
| DELETE | /auth/mfa/factors/:factorId | user token |
| GET | /auth/providers | → { google, github, apple: { available, source } } |
| GET | /auth/oauth/:provider/start?redirect_url=… | 302 → provider consent |
OAuth completes at GET/POST /api/cloud/auth/oauth/:provider/callback, which redirects back to your redirect_url with ?lc_session=<jwt> on success or ?lc_error=<code> on failure.
| Method | Path | Body / notes |
|---|---|---|
| POST | /storage/upload | { bucket?, path, content_type?, data_b64 } → { bucket, path, bytes, url } |
| GET | /storage/object?bucket=…&path=… | the file (302 to CDN for public objects) |
| POST | /storage/remove | { bucket?, path } |
| POST | /storage/create-upload-url | { bucket?, path, content_type? } → { uploadUrl, method:"PUT", headers, bucket, path } |
| POST | /storage/finalize | { bucket?, path } → { bucket, path, bytes, url } |
| Method | Path | Body |
|---|---|---|
| POST | /vector/embed | { input } → { embedding, embeddings, model, dimensions } |
| POST | /vector/search | { table, column, embedding, limit?, metric? } |
| POST | /search/text | { table, column, query, is_tsvector?, limit? } |
| POST | /search/hybrid | { table, text_column, vector_column, query, embedding, … } |
| Method | Path | Body |
|---|---|---|
| POST | /functions/:slug | { input? } → { ok, data } · builtins + custom |
| Method | Path | Notes |
|---|---|---|
| GET | /realtime?table=foo,bar | SSE (text/event-stream); emits event: change / data: { table, type, row }; ~25s heartbeat. See Realtime. |
| Method | Path | Notes |
|---|---|---|
| GET | /push/vapid-public | → { key } (VAPID public key) |
| POST | /push/subscribe | { subscription } |
| POST | /telemetry | { events: [...] } |
| GET | /config?client_id=… | → remote config values |
Backend administration — secrets, function source, provider config, schema migrations, usage — lives under a separate owner-authenticated tree (/api/cloud/account/backends/<backend-id>/…) and is driven by the console at lingcode.dev/backends, not by the public anon key. The app-facing surface above is what you call from your code.