WisyLink's API lets you create and update hosted websites from code. Upload reference files, send a plain-language message to POST /chat, and the agent builds and hosts a site at its own wisylink.com address. Send another message with the same link id to change it. API usage shares your plan's 5-hour and weekly allowance.
- A handful of REST endpoints cover the whole lifecycle: upload files, create or continue a site with
POST /chat, poll its status, list and delete sites. POST /chattalks to Wisy, the same assistant as in the app, so a reply can be a question instead of a build. Checkstatusbefore you start polling.- API keys are created on the web, and each one carries its own spend cap of up to $10 per day, week or month, on top of your plan's windows.
- The
@wisylink/clinpm package wraps the same endpoints in a command-line tool and a Node client. - Custom domains and service keys (Stripe, email) are set in the web or mobile app; the public API doesn't cover them.
What you can automate
The API is the chat, minus the chat window. Every POST /chat is a message to Wisy; when the request is clear, an AI build agent writes a server-rendered web app and deploys it to https://<id>.wisylink.com, with saved data, accounts or file uploads when the brief calls for them. There are no template, theme or page-type parameters. Everything about the site, from sections and copy to features and style, goes into the message, the same way a person would describe it. (For the concepts behind this, see what an AI app builder is.)
The base URL is https://api.wisylink.com. Authenticate with an x-api-key: <key> header or Authorization: Bearer <key>. JSON endpoints take Content-Type: application/json, and timestamps are epoch milliseconds.
| Endpoint | Method | Purpose | Key fields |
|---|---|---|---|
/files | POST | Start a file upload | name in, id out |
/files/chunks | POST | Upload the file's bytes, at most 4 MB per request | id and last=true on the final chunk; raw binary body |
/files/:id | GET | Read file metadata | content_type, name, size |
/files/:id | DELETE | Delete a file | Also removed from any link's file_ids |
/chat | POST | Create a site (no id) or continue one (with id) | message, up to 10 file_ids; returns id, url, status, answer |
/links | GET | List your sites, newest first | page, limit (1 to 100); items, total_pages, has_next |
/links/:id | GET | Read one site | status, url, file_ids |
/links/:id | DELETE | Delete a site and its reference files | Returns {"ok": true} |
What the public API does not do: set the keys a site needs for payments or email (the owner pastes those under Integrations), attach a custom domain, read or write a site's saved data, or export source code.
Get an API key and set a per-key spend limit
- Sign in at wisylink.com. Keys are created on the web only, not in the mobile apps.
- Open API Keys in your account settings and choose Generate key.
- Name it after where it will live, such as "acme-landing-pages-ci", so you know what breaks when you revoke it.
- Set the window and the cap. Pick daily, weekly or monthly and a spend limit in USD, up to $10 per window. Windows follow a UTC day, a week starting Monday, or a month.
- Copy the key immediately. It is shown once. Store it in your secret manager or CI secrets, for example as
WISYLINK_API_KEY.
The cap is a safety throttle, not your plan. Every request also draws on your account's rolling 5-hour and weekly windows, the same ones the web and mobile chat use, metered by the AI work the request triggers, including reading your reference files. The cap is checked before each request and spend is recorded after it, so the last request in a window can land slightly over. Revoking a key deletes it at once, and anything still using it gets 401.
Can you use the API on the free plan? Yes. On Community, the same rules apply as in the app: one build per 5-hour window, and sites stay live for 30 days plus a week marked for deletion. That is enough to prototype an integration; for client work or batches, a paid plan gives you more allowance, builds in parallel and links that stay live while you're subscribed.
Upload reference files (chunked uploads)
Reference files are anything the agent should read or place on the site: a brand guide, a logo, product photos, a price list. Supported formats are JPEG, PNG, WebP, GIF, HEIC, SVG, MP3, MP4, PDF, XLSX, DOCX, CSV, TXT, JSON and HTML, up to 25 MB each. The real type is detected from the bytes, and empty files are rejected.
Uploading takes two calls. First create the file and keep the returned id:
curl -s -X POST https://api.wisylink.com/files -H "x-api-key: $WISYLINK_API_KEY" -H "Content-Type: application/json" -d '{"name":"brand-guide.pdf"}'
Then send the bytes. A file of 4 MB or less goes in one request with last=true:
curl -s -X POST "https://api.wisylink.com/files/chunks?id=$FILE_ID&last=true" -H "x-api-key: $WISYLINK_API_KEY" -H "Content-Type: application/octet-stream" --data-binary @brand-guide.pdf
For larger files, split them (split -b 4m brand-guide.pdf part_), send the parts in order to the same id, and add last=true only to the final part; that call finalizes and processes the file. An upload must finish within 15 minutes or the session expires. In Node, the client described below does the chunking for you.
Create a site with POST /chat and wait for the live URL
Send the brief
Omit id to start a new site. The message can be up to 5,000 characters, which is room for a proper brief: purpose, audience, sections, real copy, features, style and language. End it with "choose sensible defaults for anything I haven't specified" to make a build on the first call more likely. Replace FILE_ID with the id from your upload.
curl -s -X POST https://api.wisylink.com/chat -H "x-api-key: $WISYLINK_API_KEY" -H "Content-Type: application/json" -d '{"message":"Build a one-page landing site for Ember, a specialty coffee roaster in Portland. Sections: hero, three single-origin coffees with prices, wholesale inquiry form saved to an admin area, opening hours, map link. Use the attached brand guide for colors and fonts. Choose sensible defaults for anything I have not specified.","file_ids":["FILE_ID"]}'
The same call from Node with fetch:
const res = await fetch("https://api.wisylink.com/chat", { method: "POST", headers: { "x-api-key": process.env.WISYLINK_API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ message: brief, file_ids: [fileId] }) }); let link = await res.json();
A new site returns 201 with id, url, status, answer, created_at and updated_at. Store the id: it is how you change, read or delete the site later. Treat it as an opaque string rather than validating its format.
Read the status before you poll
The status in the POST /chat response tells you whether a build actually started:
buildingorqueued- A build is running or waiting for a free slot (
queuedisn't in the reference's list yet, but you can see it). Start polling. On a new site, theurlserves a "building" page until the first build finishes. idleon a new site- No build started. Wisy answered instead, and
answerusually holds one short question, such as which payment provider to use. Reply with anotherPOST /chatcarrying the sameid. completedorabortedright after a follow-up- On an existing site, the status shows the latest build's outcome. If it didn't change to
building, your message got a reply, not a build; readanswer.
Poll until the build settles
There are no webhooks, so poll GET /links/:id. Builds run for minutes, not seconds, so every 15 to 30 seconds is plenty. Treat any status other than completed, aborted or idle as still running, which keeps your loop safe if a new in-progress status appears:
const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); while (!["completed", "aborted", "idle"].includes(link.status)) { await sleep(20000); link = await (await fetch(`https://api.wisylink.com/links/${link.id}`, { headers: { "x-api-key": process.env.WISYLINK_API_KEY } })).json(); }
When status is completed, url is your live site. aborted means the build stopped before finishing; a site that was already live keeps its previous version, and a new message tries again. If the site later gets a custom domain, url returns that domain instead. The build's full report, including any note that the site waits for Stripe or email keys, is in that link's chat in the web app.
Continue, list and delete links
To change a site, send only the new instruction with its id in place of LINK_ID; the server keeps the conversation history, and a continued site answers 200:
curl -s -X POST https://api.wisylink.com/chat -H "x-api-key: $WISYLINK_API_KEY" -H "Content-Type: application/json" -d '{"id":"LINK_ID","message":"Add a second location: 412 Alder St, open daily 7 am to 3 pm."}'
Poll again the same way. Each change is a new build; a failed one leaves the last good version live, while a successful one goes live as soon as it finishes, so run your checks after every change. Wait for one build to settle before sending the next change, unless the new message corrects the build that is running.
To list sites, page through GET /links?page=1&limit=100 until has_next is false. To remove one, DELETE /links/:id deletes the site and the reference files attached to it. A custom domain it used stays on your account, ready for another site.
The CLI and the Node client
The @wisylink/cli package (MIT licensed, Node 18.17 or later) maps commands one-to-one onto the endpoints above, which makes it the quickest way to script WisyLink from a shell or a CI job:
npm i -g @wisylink/cli, thenexport WISYLINK_API_KEY=...or pass--api-keyper command.wisylink files upload ./brand-guide.pdfuploads in 4 MB chunks automatically.wisylink chat --message "Build a landing page for Ember Coffee." --file-id FILE_IDcreates a site.wisylink links list --page 1 --limit 50lists your sites.
Commands print JSON, so jq can pick out the id or url. --timeout takes 1,000 to 120,000 milliseconds; raise it for chat, which waits for Wisy's reply.
The same package exports a Node client: import { CreateWisyLinkClient } from "@wisylink/cli"; const client = CreateWisyLinkClient({ apiKey: process.env.WISYLINK_API_KEY }); const file = await client.uploadFile("./brand-guide.pdf"); const link = await client.chat({ message: brief, fileIds: [file.id] });
Two version notes, as of the npm release 0.2.1. First, commands and client methods that take a link id (chat --link-id, links get, links delete, and linkId, getLink, deleteLink in Node) validate link ids against an older id format and reject current ones, so continue, read and delete sites over HTTP as shown above. Second, the CLI docs already describe a newer release with a browser login (wisylink login), an interactive chat with live build progress and a --json flag; run wisylink --version to see which one you have.
Limits, errors and good practices
Errors come back as JSON with an error code. The ones worth handling explicitly:
429 limit_reached- Your plan's 5-hour or weekly window is used up (
reasonis5h_limitorweekly_limit). Schedule the retry forreset_at, in epoch milliseconds, instead of retrying in a loop. 429 credit_limit_exceeded- This key reached its own spend cap. It can happen on any endpoint; wait for
resets_ator raise the cap (up to $10) on the web. 429 build_concurrency_limit- Too many builds are running for your plan. Wait for one to settle, then send the next.
429 rate_limited- Too many requests in a short time. Back off and retry.
400 bad_request- Invalid JSON, an empty message or one over 5,000 characters, or file ids that don't exist (listed in
missing_file_ids). Note that only the first 10file_idsare used; extra ones are dropped without an error. 401,404,500 chat_unavailable- A missing or revoked key; a link or file that isn't yours; Wisy briefly unavailable, worth one retry after a pause.
Habits that save money and confusion:
- Keep keys on the server. Never ship a key in browser or app code. Use one key per client or pipeline so each has its own cap and can be revoked alone.
- Don't blindly retry
POST /chat. It isn't idempotent: a request that timed out on your side may still have created a site or started a build. CheckGET /links(newest first) before resending, and give the call a client timeout of a minute or more. - Queue batch jobs. Run as many builds at once as your plan allows and treat
build_concurrency_limitas a signal to wait. - Review before you ship. AI output can contain mistakes. Open each site, test its forms and read its copy before a client or the public sees it; WisyLink's terms make reviewing and testing your responsibility.
Use cases: agency pages, event microsites, internal tools
Agency landing pages. Keep a brief template with slots for the client's name, offer, sections and tone, fill it from your CRM, and attach the client's logo and brand guide as files. Each campaign becomes its own site, client feedback becomes follow-up messages, and a key per client keeps spend separate. Connect the client's domain in Domains once the page is approved. A worked example of the page itself is in building a waitlist landing page with AI.
Per-event microsites. One row per event in a spreadsheet (name, date, venue, capacity) becomes one RSVP site per event, created by a script that stores each returned id next to its row. The guide to creating an event RSVP page with AI shows what to put in each brief. After the event, delete the site or keep it as an archive on a paid plan.
Internal automations. A script can create the first version of an internal tool, such as an inventory tracker or an approvals board. Don't rebuild to update data, though: ask Wisy to add an endpoint your systems can post records to, protected by a secret you set under Integrations. Records written that way reach visitors within about a minute without spending a build. More on this pattern in building internal tools with AI.
Get your key and build your first site from code
Create a free account, generate an API key with a small cap, and send your first brief with the curl call above. Keep the API reference open for field-level details, and when the integration graduates from prototype to production, pick a plan with the build allowance and parallel builds your volume needs on the pricing section.
F.A.Q.
