Project
Projects: a repo deployed to a URL, with what the running app needs.
repo = noclick.Repo.from_name("booking", create_if_missing=True)
app = noclick.Project.from_name("booking", repo=repo, create_if_missing=True)
app.bind("DB", noclick.Database.from_name("booking-db", create_if_missing=True))
app.secrets.set("MAPS_API_KEY", "...")
deployment = app.deploy().wait()
print(deployment.url) # https://booking.noclick.siteA deploy of the production branch goes live at app.url; any other branch
gets a preview URL of its own. Bindings, secrets and schedules apply from the
next deployment.
Inside the running app, noclick.Project.current() is the app itself, signed
in by its deployment (no key in the app's code), and the whole client acts as a
key of the app's user: app.database("DB") reaches what it's bound to,
noclick.Agent.from_name(..., create_if_missing=True) makes what it needs,
app.ai and app.agent("SUPPORT") think and talk as the app, and
app.verify_request(...) checks a scheduled request. What the app spends
counts toward its budget_usd_per_month ($10 unless set) and its user's cap.
class ProjectAn app: a repo deployed to a URL. Project.fromName("booking", { repo, createIfMissing: true })
is lazy; project.deploy() builds and ships it.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | string | |
| name | string | |
| url | string | Its production URL ( |
| hostname | string | Its own name under NoClick's user-sites domain ( |
| repoId | string | null | Null for an app NoClick publishes ( |
| published | boolean | NoClick publishes its files itself (an automation's interface): it has no repo, and deploys when it's published again from there. |
| workflow | { id: string; name: string | null } | null | The automation it was published from, or null for every other app. |
| runtime | ProjectRuntime |
|
| framework | string | null | On |
| productionBranch | string | The branch whose deploys go live. |
| domainNames | string[] | The live hostnames of the domains serving it, oldest first; |
| deployOnPush | DeployOnPush | What a push to its repo deploys by itself: |
| previewResources | PreviewResources |
|
| budgetUsdPerMonth | number | null | The most it may spend in a calendar month, in US dollars: $10 unless its maker set another; null only for an app NoClick publishes. |
| checkPaths | string[] | The paths a |
| region | RegionName | null | The region its server code is placed near, fixed when it was created. |
| userId | string | null | |
| createdAt | Date | null | |
| production | Deployment | null | The deployment serving |
| repo | Repo | null | The repo it deploys from (null for an app NoClick publishes). |
| secrets | ProjectSecrets | Its secrets: |
| settings | ProjectSettings | Its plain settings: |
| ProjectEmail | Transactional email from the app. | |
| payments | ProjectPayments | Payments into an account the owner claims. |
| signIn | ProjectSignIn | Sign-in for the app's own users. |
| ai | ProjectAI |
|
createstatic
static create(name: string, options?: ProjectCreateOptions): Promise<Project>Create a project (unique per user; a duplicate throws Conflict with existingId) that
deploys repo, or a new, empty repo of the same name when omitted. Nothing is deployed until deploy().
The repo it deploys (a Repo or its id); a new, empty repo of the project's name when omitted.
auto (the default) runs each deployment where its commit says: on the edge (a wrangler config
naming its entry, a JavaScript framework, or only files: server code in isolates, static files
served from everywhere) or in a container that scales to zero (a Dockerfile, or a server in
another language: Flask, Django, Go, or a Node server with a start script), and the deployment
says which and why. edge or container pins it there whatever the commit has.
On edge: nextjs, remix, astro, vite or static; on container: python, node or
docker (the repo's own Dockerfile); under auto either, which names its runtime. Detected from
the repo at each build when omitted.
The branch whose deploys go live; the repo's default branch when omitted.
The most the app may spend in a calendar month, in US dollars: hosting and builds, its calls to
what it's bound to, its email, its AI and its agent turns. Every app gets one: $10 unless you
pass another (an agent making a site sets one that fits it). At it the app's paid calls stop and
its pages keep serving; at twice it its pages pause. In fromName, null removes it.
The region its server code is placed near (regions()), for good: put it with the database it
binds, so its queries stay short. Omitted, your account's default region, else us-east; its
static files are served from everywhere. It says where the code runs, not where data stays (that's
the database's and bucket's region). An existing app elsewhere makes fromName throw Conflict
naming its region.
What a push deploys by itself: off (the default: only deploy() deploys), production (a push
to the production branch deploys it) or all (a push to any other branch deploys a preview too).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
The new project, loaded. Nothing is deployed until deploy().
fromNamestatic
static fromName(
name: string,
options?: ProjectCreateOptions & { createIfMissing?: boolean },
): ProjectA lazy reference to the project called name for the current (or given) user. With
createIfMissing your code is the definition: it's created on first use, and an existing
project whose repo, runtime, framework, production branch, budget or deployOnPush differ is
updated to match (one you leave out is never changed).
The repo it deploys (a Repo or its id); a new, empty repo of the project's name when omitted.
auto (the default) runs each deployment where its commit says: on the edge (a wrangler config
naming its entry, a JavaScript framework, or only files: server code in isolates, static files
served from everywhere) or in a container that scales to zero (a Dockerfile, or a server in
another language: Flask, Django, Go, or a Node server with a start script), and the deployment
says which and why. edge or container pins it there whatever the commit has.
On edge: nextjs, remix, astro, vite or static; on container: python, node or
docker (the repo's own Dockerfile); under auto either, which names its runtime. Detected from
the repo at each build when omitted.
The branch whose deploys go live; the repo's default branch when omitted.
The most the app may spend in a calendar month, in US dollars: hosting and builds, its calls to
what it's bound to, its email, its AI and its agent turns. Every app gets one: $10 unless you
pass another (an agent making a site sets one that fits it). At it the app's paid calls stop and
its pages keep serving; at twice it its pages pause. In fromName, null removes it.
The region its server code is placed near (regions()), for good: put it with the database it
binds, so its queries stay short. Omitted, your account's default region, else us-east; its
static files are served from everywhere. It says where the code runs, not where data stays (that's
the database's and bucket's region). An existing app elsewhere makes fromName throw Conflict
naming its region.
What a push deploys by itself: off (the default: only deploy() deploys), production (a push
to the production branch deploys it) or all (a push to any other branch deploys a preview too).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A project that loads (or is created, or brought in step with your definition) the first time it's used. Nothing is sent yet.
currentstatic
static current(options?: { client?: NoClick }): ProjectThe app this code runs in, from inside a deployment. NoClick puts the deployment's own
credential in its environment (NOCLICK_DEPLOYMENT_TOKEN), so the app's code holds no key, and
the whole client (Agent, Database, AI, ...) acts as a key of the app's user, attributed to
the app. What its code makes or buys (a phone number, a domain) is the app's own; its user's other
resources it reads and uses but never changes or deletes (on one bound to the app, it may clear its
data); it never reaches another app, another user or what another app's code made, and never mints
keys, sets a cap or budget, changes policy, answers approvals, accepts terms for a person or reads
secrets' values (PermissionDenied). What it spends, buys (each renewal too) and starts that bills
later (its numbers' calls and texts, its automations, MCP servers, channels, machines and cards)
counts toward the app's budget: a purchase must fit what's left of it, and one in a person's name
throws ApprovalRequired until they approve it. The app's code decides who may make it do what:
policy doesn't judge its calls. Outside a deployment it throws Unauthenticated; pass client to
act with a key of your own instead.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
The app, loaded on first use. Nothing is sent yet.
fromIdstatic
static fromId(projectId: string, options?: CallOptions): ProjectA lazy reference to a project by id.
A project that loads the first time you read a field. Nothing is sent yet.
getstatic
static get(projectId: string, options?: CallOptions): Promise<Project>Load a project by id now; an unknown id raises noclick.NotFound.
The project, loaded, with its production deployment.
liststatic
static list(options?: ReadOptions & { limit?: number }): Promise<Project[]>The current user's projects (the account's own outside asUser, or those userFilter covers).
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
Every matching project, newest first, fetched page by page, each with its production deployment.
listPagestatic
static listPage(options?: ReadOptions & PageOptions): Promise<Page<Project>>One page of projects; pass nextCursor back as cursor.
The next_cursor of the page before; omit it for the first page.
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A page whose data holds the projects and whose next_cursor is None on the last page.
analyticsSummarystatic
static analyticsSummary(
options?: ReadOptions & { period?: AnalyticsPeriod; tz?: string },
): Promise<AnalyticsSummary>Every app's requests over the last period (7d by default) and how many failed, in one read
however many apps there are: each production site's, over the same window analytics reads, its
days starting in tz (UTC when omitted). Which apps: the current user's, or those userFilter
covers, as list. Requests, not visitors.
"7d"Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
Its AnalyticsSummary: one line per app, newest first.
update
update(
changes: {
repo?: Repo | string;
runtime?: ProjectRuntime;
framework?: string | null;
productionBranch?: string;
budgetUsdPerMonth?: number | null;
deployOnPush?: DeployOnPush;
previewResources?: PreviewResources;
checkPaths?: string[];;
},
): Promise<this>Change the repo it deploys, its runtime, its framework (null: detected at each build), its
productionBranch, its budgetUsdPerMonth (an app with code always has one: null throws InvalidRequest), what a push deploys
(deployOnPush), the paths a promote checks first (checkPaths, up to 10, each starting with
/; [] stops checking) or whether previews get their own resources (previewResources:
branch or shared). A new repo, runtime, framework or branch applies from the next deployment
(what's live keeps serving where it runs until then), the rest at once; its user's cap still
applies either way. A field you leave out is unchanged. Pinning a runtime the app's framework
doesn't run on throws InvalidRequest: change the framework with it.
This app, updated. A field you leave out is unchanged.
deploy
deploy(
options?: { branch?: string; commit?: string },
): Promise<Operation<Deployment>>Build and deploy branch (the production branch when omitted) at its head, or at commit.
wait() resolves to the ready Deployment; a failed build rejects with OperationFailed,
whose details.deployment_id names the deployment to read the build log of.
An operation whose .wait() gives the Deployment, ready. A failed build raises
noclick.OperationFailed, whose deployment_id names the deployment to read the build log of.
deployments
deployments(
options?: { branch?: string; limit?: number },
): Promise<Deployment[]>Its deployments, newest first: limit of them (20 by default), of every branch or of branch.
Return at most this many.
20)Up to limit deployments (20 by default; None for all), fetched page by page.
rollback
rollback(to: Deployment | string): Promise<Operation<Deployment>>Serve an earlier ready deployment at url again, without rebuilding: one routing change, live
within a minute (to.promote()). It covers the app's last 10 production deployments; an older one
is built again from its commit.
An operation whose .wait() gives that deployment (or, past the window, the one built
from its commit), now production.
bind
bind(
name: string,
resource: Bindable | string,
options?: { kind?: BindingKind },
): Promise<Binding>Give the app resource under name: a Database, Bucket, Connection, Dict, Queue,
Secret or Agent, or its id with kind. Binding a name again replaces what it pointed at; it
takes effect at the next deployment. An agent is the app's user's own or one of your account's,
and the app talks to it as itself (see agent).
The binding, naming the resource.
bindings
bindings(): Promise<Binding[]>What the app is given, by name.
Each binding with the resource it names.
unbind
unbind(name: string): Promise<void>Take name away from the app at the next deployment; the resource itself is kept.
database
database(name: string): Promise<Database>The database bound to the app as name (from the app's own code, Project.current(), or yours).
The noclick.Database; a name bound to something else raises noclick.InvalidRequest,
an unbound one noclick.NotFound.
bucket
bucket(name: string): Promise<Bucket>The bucket bound to the app as name.
The noclick.Bucket.
dict
dict(name: string): Promise<Dict>The dict bound to the app as name.
The noclick.Dict.
queue
queue(name: string): Promise<Queue>The queue bound to the app as name.
The noclick.Queue.
connection
connection(name: string): Promise<Connection>The connection bound to the app as name: the app runs its operations as the account that connected it.
The noclick.Connection.
agent
agent(name: string): Promise<Agent>The agent bound to the app as name. From the app's own code it acts as the app: message,
send and ask start turns in the app's own conversations, and history and conversations
read only those. A key the app passes (visitor-91c2) always names a thread of the app's
(app:<app id>:visitor-91c2 to your own key, which reads it; no other app reaches it). The
agent's policy still judges its tool calls, and its turns are billed like any turn, attributed to
the app. Anything else (update, delete, its triggers, memory, wake-ups or children) throws
PermissionDenied from the app, unless its code made the agent. A preview's code talks to it in
threads of the preview's own (app-preview:<app id>:<key>), its tools answering from a simulated
world; no app's code talks to an agent holding the noclick tool.
const assistant = await Project.current().agent("ASSISTANT");
const reply = await assistant.message(text, { conversation: visitorId });The noclick.Agent.
verifyRequest
verifyRequest(
payload: string | Uint8Array | ArrayBuffer,
headers: WebhookHeaders,
options?: { toleranceS?: number },
): Promise<void>Check that a request to the app came from one of its schedules: payload is the raw body exactly
as it arrived, headers its headers. Its NoClick-Signature is checked against the app's signing
secret, which NoClick sets in the deployment's environment (NOCLICK_SIGNING_SECRET); a timestamp
more than toleranceS (300) from now is refused. Sends nothing; throws WebhookSignatureError.
await app.verifyRequest(await request.text(), request.headers);300schedule
schedule(
cron: Cron,
options: { path: string; name?: string },
): Promise<Schedule>POST path on the app on every tick of cron. A schedule is keyed by name (the path when
omitted), so calling this again with the same one updates it rather than adding another.
The schedule, active, with its next_run.
schedules
schedules(): Promise<Schedule[]>Its schedules.
Each schedule with its status, next_run and how its last_run went.
addDomain
addDomain(name: string): Promise<Domain>Serve the app at the domain name too: pointed at once if bought through NoClick (at its name
and www), else connected and pending_dns until its records are set; each name is live once
its certificate is issued. A subdomain of a domain your account holds (ana.acmebooking.com)
can serve any project, a user's included, from a key acting for the whole account: its record is
written under a bought domain, none is needed under one connected with wildcard (live within
minutes), and records lists it otherwise.
The noclick.Domain, with its status.
domains
domains(): Promise<Domain[]>The domains that serve the app.
Each noclick.Domain with its status.
removeDomain
removeDomain(name: string): Promise<void>Stop serving the app at name (a bare domain's www too). The domain itself is kept: a bought
one stays yours, and one connected for the app stays connected until you delete it.
logs
logs(options?: ProjectLogsOptions): Promise<LogLine[]>What the app printed while serving, oldest first; lines are kept 7 days.
The first limit lines after this time; the last limit lines when omitted.
One deployment's lines (a Deployment or its id).
100 by default, at most 1,000.
100)The lines. Pass the last one's at as since to read on from there.
analytics
analytics(options?: ProjectAnalyticsOptions): Promise<ProjectAnalytics>How the app's traffic went over the last period (24h, 7d by default, or 30d): its
requests each hour (24h) or day, starting in tz (an IANA time zone; UTC when omitted), how
many failed, the totals against the same length of time before, by status, how long answers
took to begin and the busiest paths. environment: its production site (the default), its
previews (preview) or both (all). Every request its sites answered counts, its static files
and API calls included, people's and bots' alike: requests, not visitors.
7d by default.
"7d")Its production site (the default), its previews, or both.
"production")The IANA time zone its hours and days start in (Europe/Paris); UTC when omitted.
Its ProjectAnalytics, read from NoClick's record of each request.
usage
usage(): Promise<ProjectUsage>What the app has spent this month, against its budget: everything it cost in usd, split
byType, and limitedBy, what refuses its paid calls now.
Requests served, compute in cpu_ms, usd and its budget_usd, what refuses its paid
calls (limited_by), and the period_start it counts from.
export
export(): Promise<Operation<ExportBundle>>Package everything the app is, to run it elsewhere: its code as a git bundle, apps/<name>.json
with its settings, bindings and secret names (never values), and each database, bucket, dict and
queue it binds, each in its own export's format. wait() resolves to the ExportBundle. To put
the code on GitHub, use the repo's exportToGithub.
An operation whose .wait() gives an noclick.ExportBundle.
delete
delete(options?: { deleteData?: boolean }): Promise<TrashItem>Delete the app: it goes offline within seconds and to the trash, restorable for 30 days with everything
it had (releases, settings, secrets, bindings, schedules, domains), served again with no build.
The repo and bound data are kept unless deleteData, which moves the repo to the trash too, with
whatever no other app binds. Returns its TrashItem.
Its TrashItem: restore() serves it again where it was.
Deployment
class DeploymentOne build of a project at one commit. status goes queued, building, then ready
(serving at url), failed (error says why) or canceled. production is true for the one
serving the project's own URL.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | string | |
| projectId | string | |
| branch | string | |
| commit | string | null | The sha it was built from. |
| commitMessage | string | null | |
| status | DeploymentStatus | |
| url | string | null | Where this deployment serves (a preview URL unless it's production). |
| production | boolean | |
| environment | DeploymentEnvironment |
|
| startedBy | "deploy" | "push" |
|
| error | string | null | Why the build failed, or why a push couldn't deploy, in one line (null unless |
| runtime | DeploymentRuntime | null | Where it runs: |
| runtimeReason | string | null | Why it runs there, in words: the app's pinned runtime, or what its commit has ("a Dockerfile describes its server"). |
| createdAt | Date | null | |
| readyAt | Date | null | |
| lastCheck | JsonObject | null | Its latest check, in short: |
fromIdstatic
static fromId(deploymentId: string, options?: CallOptions): DeploymentA lazy reference to a deployment by id.
A deployment that loads the first time you read a field. Nothing is sent yet.
getstatic
static get(deploymentId: string, options?: CallOptions): Promise<Deployment>Load a deployment by id now; an unknown id raises noclick.NotFound.
The deployment, loaded.
logs
logs(
options?: { kind?: "build" | "runtime"; limit?: number },
): Promise<LogLine[]>Its build output (kind: "build", the default) or what it printed while serving ("runtime"), oldest first.
"build"Return at most this many.
The lines, or the last limit of them. A build keeps every line; runtime lines are kept 7 days.
cancel
cancel(): Promise<this>Stop a deployment that's still queued or building; one that already finished is left as it is.
This deployment, canceled unless it had already finished.
check
check(paths?: string[]): Promise<Operation<DeploymentCheck>>Load paths of this ready deployment where it serves (its preview URL, or the app's URL in
production) in a browser: each one's HTTP status, load time, console errors and a screenshot
kept as a file the chat shows. Without paths, the app's checkPaths (else its home page). A
deployment no URL serves now can't be checked (Conflict). The browser is billed at its
provider's price, by the second. wait() resolves to a DeploymentCheck.
const checked = await (await preview.check(["/", "/book"])).wait();
for (const page of checked.paths) console.log(page.path, page.status, page.problem, page.screenshot?.url);An operation whose .wait() gives a DeploymentCheck; passed is False when a path answered
400 or more, didn't load, or a script on it threw.
requests
requests(options?: { since?: Date | string }): Promise<RequestOutcomes>How the requests this deployment served went since since (the last 24 hours when omitted, at
most 7 days back): how many, by status, the error rate and the slowest routes, from what its
host recorded of each request.
Its RequestOutcomes, read from NoClick's record of each request it answered, files included.
promote
promote(options?: { force?: boolean }): Promise<Operation<Deployment>>Make this ready deployment the production one; its URL starts serving this build within a
minute. A preview is released again with production's secrets, without a build. Instant
rollback covers the app's last 10 production deployments, and each branch's newest preview for
30 days; promoting an older one builds its commit again (a project.deploy operation). wait()
resolves to the deployment now production.
An app with checkPaths checks the deployment first and doesn't promote one that fails (the
operation fails with the check in its error; production keeps serving); force: true promotes
it without the check. The databases the app binds are bookmarked first: undoing the promote
(Operation.undo()) puts them back as they were, with the release before.
An operation whose .wait() gives this deployment (or, past the window, the one built
from its commit), now production.
Schedule
class ScheduleA project's cron job: NoClick POSTs path on the app on every tick of cron (read in tz).
When 5 runs in a row fail the same way over at least 30 minutes it pauses itself (pausedReason
says why) until resume().
- Like every live object, it also has hydrate(), refresh() and raw.
| id | string | |
| projectId | string | |
| name | string | What keys it within the project (the path unless you named it). |
| cron | string | The five-field cron expression. |
| tz | string | |
| path | string | |
| status | "active" | "paused" |
|
| pausedReason | string | null | |
| nextRun | Date | null | |
| lastRun | Date | null | |
| lastStatus | "succeeded" | "failed" | null |
|
| lastError | string | null | Why the last run failed, in one line ( |
| createdAt | Date | null |
fromIdstatic
static fromId(scheduleId: string, options?: CallOptions): ScheduleA lazy reference to a schedule by id.
A schedule that loads the first time you read a field. Nothing is sent yet.
getstatic
static get(scheduleId: string, options?: CallOptions): Promise<Schedule>Load a schedule by id now; an unknown id raises noclick.NotFound.
The schedule, loaded.
pause
pause(): Promise<this>Stop firing until resume().
This schedule, paused.
resume
resume(): Promise<this>Fire again from the next tick, clearing pausedReason.
This schedule, active, with its next_run.
runNow
runNow(): Promise<this>Call its path once now and wait for the answer (at most 30 seconds); a paused schedule stays paused.
This schedule, with last_run and last_status from that call.
delete
delete(): Promise<void>Delete the schedule; it stops firing.
ProjectSecrets
class ProjectSecretsproject.secrets: values the running app reads from its environment by name, set for production
deployments, previews or both. Values are write-only; nothing reads one back. An app's environment
holds 128 values in all, NoClick's own and each bound secret's included.
set
set(
name: string,
value: string,
options?: { environment?: SecretEnvironment },
): Promise<void>Set name to value (at most 5 KB), replacing any value it had, for environment:
production (deploys of the production branch, and what's promoted), preview (every other
branch) or all (the default). It reaches the app at the next deployment; keep a larger value in
a bound Secret or bucket the app reads.
list
list(
options?: { environment?: SecretEnvironment },
): Promise<Record<string, Date | null>>Each secret set for environment (all by default) and when its value was last set, never the value.
Each secret's name mapped to when its value was last set.
delete
delete(
name: string,
options?: { environment?: SecretEnvironment },
): Promise<void>Delete name from environment (all by default); the app stops seeing it at the next deployment.
versions
versions(
name: string,
options?: { environment?: SecretEnvironment },
): Promise<Version[]>What name held before each change or deletion of the last 30 days, newest first, in
environment (all by default), never the values.
Each earlier version, with its environment.
restore
restore(name: string, version: Version | string): Promise<void>Set name back to version's value in its environment (deleted since, it's back).
ProjectSettings
class ProjectSettingsproject.settings: plain values the running app reads from its environment by name, such as a
feature flag or a public URL. Unlike secrets they read back and show in the console.
set
set(name: string, value: string): Promise<void>Set name to value (at most 4 KB); it reaches the app at the next deployment.
list
list(): Promise<Record<string, string>>Each setting's name and value.
Each setting's name mapped to its value.
delete
delete(name: string): Promise<void>Delete name; the app stops seeing it at the next deployment.
versions
versions(name: string): Promise<Version[]>What name held before each change or deletion of the last 30 days, newest first, with each value.
Each earlier version.
restore
restore(name: string, version: Version | string): Promise<string>Set name back to version's value (deleted since, it's back); returns the value it holds again.
The value it holds again.
ProjectAI
class ProjectAIproject.ai: AI acting as the app, from the app's own code (Project.current()). Every call is
billed to your account and attributed to the app and its user: it counts toward the app's
budgetUsdPerMonth and its user's cap, and throws SpendCapExceeded once either is used up
(PaymentRequired once your account's balance is). Only the app acts as the app: on a project
reached with your own key, each call throws InvalidRequest (use AI there, for the app's user).
| baseUrl | string | The OpenAI-compatible endpoint, for an OpenAI client in the app's code; pair it with |
chat
chat(
messages: string | readonly (AIChatMessage | string)[],
options?: Omit<AIChatOptions, "user" | "client">,
): Promise<ChatReply>AI.chat as the app; model: "agent:<name>" reaches only an agent bound to the app, in its own conversations.
An OpenRouter id with the openrouter/ prefix, or agent:<name> for one of your agents; NoClick's default when omitted.
Ask for JSON: { type: "json_schema", json_schema: ... }.
OpenAI-format tool definitions, such as await toolkit.definitions({ format: "openai" }). An agent takes none.
An agent's conversation to continue (reply.conversation); only the messages after the last assistant message are sent.
The answer: its text, the message to append, and what it cost.
embed
embed(
texts: string | readonly string[],
options?: { model?: string; dimensions?: number },
): Promise<Embeddings>AI.embed as the app.
One vector per text, in order, with their length and what the call cost.
image
image(
prompt: string,
options?: { model?: string; size?: string },
): Promise<GeneratedImage>AI.image as the app.
The image: a signed url that lasts 24 hours, its size and what it cost.
transcribe
transcribe(
audio: string | Uint8Array | ArrayBuffer | Blob,
options?: {
model?: string;
language?: string;
speakers?: boolean | string[];
},
): Promise<Transcript>AI.transcribe as the app: an https URL to a recording, or its bytes.
The transcript: its text, timed segments, the language and how long the audio was.
models
models(options?: { kind?: AIModelKind }): Promise<AIModel[]>The models the app can call; kind: "agent" lists the agents bound to it.
Every matching model with its prices in US dollars, the default of each kind first.
headers
headers(): Promise<Record<string, string>>The deployment's credential as the bearer, for an OpenAI client's defaultHeaders: every call is the app's.
Authorization.
Binding
interface BindingA resource the running app reads by name.
| name | string | |
| kind | BindingKind | |
| resourceId | string | |
| resourceName | string | null | |
| createdAt | Date | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
LogLine
interface LogLineOne line an app printed, or its build did.
| at | Date | null | |
| level | "info" | "warn" | "error" | |
| message | string | |
| deploymentId | string | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
ProjectUsage
interface ProjectUsageWhat an app spent this month so far, against its budget.
| requests | number | |
| cpuMs | number | |
| usd | number | Everything the app spent: hosting, builds, its calls to what it's bound to, its email, its AI and its agent turns. |
| periodStart | Date | null | |
| budgetUsd | number | null | Its |
| limitedBy | "project" | "user" | "account" | null | What refuses its paid calls now: its own budget, its user's cap or your account's balance; null while they go through. |
| byType | Record<string, number> |
|
| raw | JsonObject | The response JSON exactly as the API sent it. |
DeploymentCheck
interface DeploymentCheckWhat Deployment.check() saw where the deployment serves (url): passed when every path did.
| deploymentId | string | |
| url | string | |
| passed | boolean | |
| paths | PathCheck[] | |
| checkedAt | Date | null | |
| costUsd | number | What the browser cost: its provider's price, by the second. |
| raw | JsonObject | The response JSON exactly as the API sent it. |
PathCheck
interface PathCheckOne path a check loaded in a browser. ok is false when it answered 400 or more, didn't load
within 30 seconds, or a script threw and nothing caught it (problem says which); other console
errors are reported without failing it.
| path | string | |
| url | string | Where it ended up. |
| ok | boolean | |
| problem | string | null | |
| status | number | null | |
| loadMs | number | null | From the navigation to the load event (null when it didn't load in time). |
| title | string | null | |
| consoleErrors | ConsoleError[] | Each |
| screenshot | JsonObject | null | The screenshot, an account file the chat shows: |
| raw | JsonObject | The response JSON exactly as the API sent it. |
RequestOutcomes
interface RequestOutcomesHow the requests a deployment served between since and until went. errors counts answers
of 500 or more and requests never answered ("none" in byStatus); sampled is true when there
were more than the 2,000 newest it read, or the record of a very busy app is a sample, each request
in it counted for the ones it stands for.
| requests | number | |
| errors | number | |
| errorRate | number | null | |
| byStatus | Record<string, number> | |
| slowestRoutes | RouteTiming[] | |
| sampled | boolean | |
| since | Date | null | |
| until | Date | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
RouteTiming
interface RouteTimingOne route's requests: how many, how many failed, and their times in milliseconds.
| method | string | |
| path | string | |
| requests | number | |
| errors | number | |
| p50Ms | number | |
| p95Ms | number | |
| maxMs | number |
ProjectAnalytics
interface ProjectAnalyticsHow an app's traffic went over a period, from since up to until: the requests its sites
answered and the errors among them (an answer of 500 or more, or none) with their errorRate,
byStatus ({ "200": 1180, "404": 21, none: 1 }), how long answers took to begin (p50Ms,
p95Ms), each interval's (hour or day, starting in tz) in series, the busiest
topPaths, and the same length of time just before since (previousRequests,
previousErrors). Counted from NoClick's record of every request, its files and API calls
included: requests, not visitors. sampled: a very busy app's record is a sample, each request in
it counted for the ones it stands for.
| period | AnalyticsPeriod | |
| environment | NonNullable<ProjectAnalyticsOptions["environment"]> | |
| interval | "hour" | "day" | |
| tz | string | |
| since | Date | null | |
| until | Date | null | |
| requests | number | |
| errors | number | |
| errorRate | number | null | |
| byStatus | Record<string, number> | |
| p50Ms | number | null | |
| p95Ms | number | null | |
| series | TrafficInterval[] | |
| topPaths | PathTraffic[] | |
| previousRequests | number | |
| previousErrors | number | |
| sampled | boolean | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
TrafficInterval
interface TrafficIntervalOne hour or day of an app's traffic: the requests from at on, and how many of them failed.
| at | Date | null | |
| requests | number | |
| errors | number |
PathTraffic
interface PathTrafficOne of an app's busiest paths: its requests, how many failed, and their 95th-percentile time in milliseconds.
| path | string | |
| requests | number | |
| errors | number | |
| p95Ms | number | null |
AnalyticsSummary
interface AnalyticsSummaryEvery app's requests over a period (from since up to until, days starting in tz), read at
once: apps newest first, an app nobody reached at 0. Requests, not visitors. sampled: a very busy
app's record is a sample, each request in it counted for the ones it stands for.
| period | AnalyticsPeriod | |
| tz | string | |
| since | Date | null | |
| until | Date | null | |
| apps | AppRequests[] | |
| sampled | boolean | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
of
of(app: Project | string): AppRequestsOne app's line (a Project or its id); one the summary didn't cover reads as 0.
The AppRequests of app (a Project or its id); one the summary didn't cover reads as 0.
AppRequests
interface AppRequestsOne app's requests to its production site over a summary's period, and how many failed.
| projectId | string | |
| requests | number | |
| errors | number |
ProjectEmail
class ProjectEmailapp.email: transactional email from the app's own address
(receipts, booking confirmations), rate-capped and charged per recipient. One-time sign-in
codes go through sendCode, on a lane of their own; newsletters and announcements through
broadcast, on a stream of their own.
enable
enable(options?: EmailEnableOptions): Promise<EmailSetup>Turn email on, or change what you pass (the rest keeps its value). With a domain, broadcasts go
from the same name on its news. subdomain. The first call answers pending while NoClick sets
up the app's sending (what it sends meanwhile goes out once that's done). A domain bought through
NoClick gets its DNS records written and turns active within minutes; any other is connected
for you and records lists what to add (within seconds of this call).
A Domain or its name; without one the app sends from its default address. A subdomain of a
domain your account holds may send any project's mail, a user's included, from a key acting for
the whole account.
On domain: noreply@<domain> unless you pass one.
The address every broadcast's footer carries, as the law asks of bulk mail; broadcast needs one.
The setup: active when the app can send now, pending while NoClick sets up
its sending (what it sends meanwhile goes out once that's done), or pending_dns
while the domain's records aren't seen yet.
status
status(): Promise<EmailSetup>How the app sends email, with the domain's records checked again (off if never enabled).
The current setup; status is off if email was never enabled.
send
send(options: EmailSendOptions): Promise<SentEmail>Send one message; ActionRequired while the domain's records are pending, RateLimited past the daily limit.
Makes a retried send safe: the same key sends once.
One address, or up to 10 that each receive the message.
The message as accepted, usually queued; messages() shows whether it
was delivered.
sendCode
sendCode(
to: string,
code: string,
options?: EmailCodeOptions,
): Promise<SentEmail>Send a one-time code (4-12 letters and digits) to to in a short message NoClick writes in the
app's name. Codes have a daily limit of their own, apart from dailyLimit; one address gets at
most 5 in 15 minutes (RateLimited). Each is charged as one message. app.signIn sends its
codes this way.
sign_in (the default) or verify_email.
"sign_in")Said in the message: 60-86400 seconds.
Makes a retried create safe: the same key returns the first result. Generated when omitted.
The message, kind code; its subject names the purpose, never the code.
messages
messages(
options?: {
to?: string;
limit?: number;
environment?: "production" | "preview";
},
): Promise<SentEmail[]>The messages the app sent, newest first: 50 unless limit says (to keeps one address's,
environment those its production or its previews sent). A preview's code reaches only the
account's own people, so environment: "preview" is its outbox: each message names the
recipients it kept in captured, with the text and html it would have sent.
Return at most this many.
50)Up to limit messages (50 unless you say; None reads them all), each with
its delivery status, whether it was opened and why it bounced.
broadcast
broadcast(options: EmailBroadcastOptions): Promise<Operation<Broadcast>>Send one message to many people who asked for it (a newsletter, an announcement). Anyone who
unsubscribed from the app's broadcasts (until they opt in again and you resubscribe them),
bounced or complained is skipped. It goes out on a bulk stream of its own under
broadcastDailyLimit, from broadcastFromAddress, so a campaign never pauses transactional
mail; it needs a domain of the app's own (ActionRequired on the default sender) and goes out
at the pace mail can be sent, so a large one takes minutes.
Every message carries its recipient's own List-Unsubscribe header with one-click unsubscribe,
and a footer with an unsubscribe link and the postalAddress (ActionRequired until there is
one). wait() resolves to the Broadcast.
Names the broadcast: sending again with the same key sends nothing new. Required.
Up to 50,000 addresses; listing them says each one opted in to mail from the app.
An operation whose .wait() gives the Broadcast: how many it was sent to and who
was skipped, and why.
resubscribe
resubscribe(address: string): Promise<boolean>Lift address's unsubscribe from the app's broadcasts once they've asked for them again and
confirmed it (a click on a link you emailed that address); call it only on such a confirmed
opt-in. A bounce or a spam complaint isn't lifted. Resolves to true if the address was
unsubscribed, false if it wasn't.
True if the address was unsubscribed and now gets broadcasts again, False if it
wasn't unsubscribed.
EmailSetup
interface EmailSetupHow an app sends email. status is off (never enabled), pending (NoClick is setting up the
app's sending, usually within a minute; mail sent meanwhile goes out once it's set up, and
pendingReason says why when setting it up failed), pending_dns (the domain's records aren't
seen yet: a domain bought through NoClick turns active within minutes), active, or paused
(too many bounces or spam complaints; pausedReason says which: bounce_rate, complaint_rate,
mailbox_provider_feedback or blocklisted). Broadcasts go out apart, from the domain's news.
subdomain, with a status of their own.
| status | "off" | "pending" | "pending_dns" | "active" | "paused" | |
| fromAddress | string | null | The address mail goes out from, like |
| fromName | string | null | The display name recipients see, like |
| domain | string | null | The sending domain; null while the app uses its default sender. |
| records | DnsRecord[] | What DNS must hold for mail to send and land: DKIM and a bounce subdomain (MX and SPF) for the
domain and its |
| dailyLimit | number | null | How many messages the app may send a day; it grows as its mail keeps landing. |
| sentToday | number | null | Sent since midnight UTC. |
| pausedReason | string | null | |
| pendingReason | string | null | Why the app's sending isn't set up yet, once setting it up failed (NoClick keeps trying); else null. |
| postalAddress | string | null | The postal address every broadcast's footer carries (null until you give one). |
| broadcastDailyLimit | number | null | How many recipients its broadcasts may reach a day, apart from |
| broadcastStatus | "off" | "needs_domain" | "pending" | "pending_dns" | "active" | "paused" |
|
| broadcastFromAddress | string | null | The address broadcasts go out from, on the |
| broadcastSentToday | number | null | Broadcast recipients sent to since midnight UTC. |
| broadcastPausedReason | string | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
| canSend | boolean | Whether |
SentEmail
interface SentEmailOne message an app sent; error says why it bounced or failed. complained is best-effort: some
mailbox providers, Gmail among them, report no single complaint. Sent to several recipients, it
shows the worst any met (a bounce, a complaint), else how far the slowest got.
A preview's code reaches only the account's own people (its owner and the members of the owner's
workspaces): every other copy is kept, never sent or charged, and named in captured; a message
whose every copy was kept is captured, with the text and html it would have sent.
messages({ environment: "preview" }) is the preview's outbox.
| id | string | |
| to | string[] | |
| subject | string | null | |
| status | "queued" | "sent" | "delivered" | "bounced" | "complained" | "failed" | "captured" | |
| opened | boolean | null | Whether it was opened, as far as can be told (privacy proxies open some mail on the reader's behalf); null for a message with no HTML, or a sign-in code, which aren't tracked. |
| bounced | boolean | |
| error | string | null | |
| at | Date | null | When it was sent. |
| kind | "message" | "code" | "broadcast" |
|
| environment | "production" | "preview" | Where the code that sent it runs. |
| captured | string[] | The recipients a preview kept rather than sent to. |
| text | string | null | What a kept message would have said; null once a message went out. |
| html | string | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
Broadcast
interface BroadcastOne broadcast an app sent: how many it was sent to, and those it skipped, each { to, reason }
with reason unsubscribed, bounced, complained or invalid. From a preview, only the
account's own people get it: captured counts the copies kept.
| id | string | |
| subject | string | null | |
| sent | number | |
| skipped | JsonObject[] | |
| captured | number | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
ProjectPayments
class ProjectPaymentsapp.payments: take payments without a payments account of your
own. NoClick opens a test account and puts its keys in the app's secrets; the owner claims
it to take real money.
enable
enable(options?: PaymentsEnableOptions): Promise<PaymentsSetup>Open the app's test account (country required the first time; NotAvailable for one the
processor doesn't serve), or answer the one it has. After testExpiresAt passes unclaimed it
opens a fresh one with new keys; a disconnected account reconnects once the owner reinstalled
NoClick's payments app.
Where the owner's business is (ISO 3166-1 alpha-2): required the first time, fixed after.
Who the processor reminds before an unclaimed test account expires (your account's email unless given).
Prefills the claim (the app's name unless given).
The setup in test mode, with the claim_url to send the owner.
status
status(): Promise<PaymentsSetup>Where the app's payments stand: mode moves from test to claimed to live; processor is there even while off.
The current setup; mode moves from test to claimed to live. processor
is there even while off, so an app's payment code can be written before enable().
PaymentsSetup
interface PaymentsSetupWhere an app's payments go. mode is off, test (a test account waiting to be claimed),
claimed (the owner is finishing the steps claimStep names), live (live keys are in the
app's production secrets and the account can take payments) or disconnected (the owner removed
NoClick's payments app or its keys). Money goes straight to the owner's account; NoClick takes no cut.
| mode | "off" | "test" | "claimed" | "live" | "disconnected" | |
| claimStep | "claim" | "application" | "install" | "verification" | null | While claiming: |
| nextStep | string | null | What to do now, in a sentence. |
| claimUrl | string | null | Where the owner claims the account: a NoClick link that always leads to their next step, working until |
| testExpiresAt | Date | null | When the test account and its data are deleted unless claimed; after that |
| dashboardUrl | string | null | The owner's own dashboard at the payment processor, once claimed. |
| secrets | string[] | The project secrets that hold the account's keys: live keys in production once |
| processor | string | null | Which payment processor apps are paid through, by the name its own client library goes by, so the
app's code can pick the library that reads |
| country | string | null | Where the business is (ISO 3166-1 alpha-2), fixed when the account was opened. |
| webhookUrl | string | null | Where the processor delivers the account's events: the app's |
| raw | JsonObject | The response JSON exactly as the API sent it. |
ProjectSignIn
class ProjectSignInapp.signIn: accounts for the app's own users. Enabling it
commits sign-in code into the app's repo and keeps the users in the app's bound database.
enable
enable(
providers: readonly SignInProvider[],
options?: SignInEnableOptions,
): Promise<Operation<SignInSetup>>Commit sign-in for providers to branch (the default branch unless given), create
usersTable (users) and the tables it needs in the bound database and deploy; nextjs,
remix and astro apps, and a static site as it is, with a worker in front of its files
(NotAvailable for client-only vite, or a site already running its own worker). google,
apple and microsoft sign in through NoClick with nothing to set up (google and microsoft
on your own client when named in ownCredentials); github uses your own client. A file holding
edits of yours is never overwritten: the change goes on a branch of its own. wait() gives the
SignInSetup.
Of google and microsoft, those that sign in with your own client at the provider, to show your own
name on its screen (set its id and secret as the app's secrets: missingSecrets). The rest, and apple,
sign in through NoClick, with nothing to set up; github always uses your own client. Omitted, each
provider keeps how it signs in now.
"users"The binding of the database to keep users in; needed only when the app binds more than one.
An operation whose wait() gives the SignInSetup once the code is
committed and deployed.
upgrade
upgrade(options?: { branch?: string }): Promise<Operation<SignInSetup>>Move the sign-in code to the library version NoClick ships now (updateAvailable), with the
same providers and the same care for edited files. wait() gives the SignInSetup.
An operation whose wait() gives the SignInSetup.
status
status(): Promise<SignInSetup>The app's sign-in: its providers, where its users are kept and what it still needs (off if never enabled).
The current setup; status is off if sign-in was never enabled.
SignInSetup
interface SignInSetupAn app's sign-in. status is off, installing (a change is being committed and deployed),
active or failed (the first change failed; a later one that fails leaves sign-in active, with
error). The code lives in the app's repo, the users in usersTable.
| status | "off" | "installing" | "active" | "failed" | |
| providers | string[] |
|
| managedProviders | string[] | The providers that sign in through NoClick, with nothing to set up at the provider (its screen names
NoClick). The rest of |
| usersTable | string | null | |
| tables | string[] | Every table sign-in keeps: |
| database | string | null | The binding of the database the users are kept in. |
| library | string | The open-source library the code is written with, and its version. |
| version | string | null | |
| updateAvailable | string | null | A newer version NoClick ships ( |
| commit | string | null | The commit that added or last changed sign-in. |
| branch | string | null | Where that commit went when it wasn't the production branch (yours, or one NoClick made beside your edits). |
| editedFiles | string[] | Files holding edits of yours that the last change left alone, committing on |
| callbackUrls | Record<string, string> | Where each provider on your own client sends people back to; register it with that provider. |
| missingSecrets | string[] | Project secrets a provider on your own client still needs ( |
| secrets | string[] | The secrets NoClick writes itself and never shows: |
| error | JsonObject | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |