Quick start
A 15-minute path from a fresh workspace to a working integration. We'll generate a scoped key, make our first authenticated call, paginate through projects, and end with a small billing reconciliation script you can lift into production.
Prerequisites
- An active Worksible workspace on Professional or higher.
- The owner or admin role on that workspace (members can't mint keys).
- A terminal with
curland your favourite scripting runtime (Node 18+, Python 3.10+, Go 1.21+ all work fine).
1 · Generate an API key
Open Worksible OS, go to Settings → API keys, and click New API key. Pick a recognisable name (the integration name is a good default), keep Live environment, and either grant Full access or pick the scopes you need. The plaintext key is shown once; copy it into your secret manager before you close the dialog.
wsk_live_AbCd1234_xR9kPmZ7nQ2vL5sH8wY3tF6jD1eB0aN4cV9uG7iM2 · Make your first call
The simplest call hits /v1/companies/me and returns the organization the key belongs to. It requires the company:read scope, which every key has by default.
curl https://api.worksible.com/v1/companies/me \
-H "Authorization: Bearer $WORKSIBLE_API_KEY"const res = await fetch("https://api.worksible.com/v1/companies/me", {
headers: { "X-API-Key": process.env.WORKSIBLE_API_KEY },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const company = await res.json();
console.log(company.name, company.plan);import os, requests
r = requests.get(
"https://api.worksible.com/v1/companies/me",
headers={"X-API-Key": os.environ["WORKSIBLE_API_KEY"]},
timeout=10,
)
r.raise_for_status()
print(r.json())req, _ := http.NewRequest("GET", "https://api.worksible.com/v1/companies/me", nil)
req.Header.Set("X-API-Key", os.Getenv("WORKSIBLE_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()
var company struct {
ID, Name, Plan string
}
json.NewDecoder(resp.Body).Decode(&company)A successful response looks like this:
{
"id": "org_2024_AbCd",
"name": "Acme Corp",
"legalName": "Acme Corporation S.L.",
"countryCode": "ES",
"vatId": "ESB12345678",
"type": "company",
"kybStatus": "approved",
"plan": "global",
"subscriptionStatus": "active",
"createdAt": "2024-08-12T09:31:24.000Z"
}3 · Paginate through projects
Lists in the Worksible API use page and perPage. Every list response includes a total so you can compute the page count up front. Loop until you've fetched everything; don't guess based on the items length.
async function listAllProjects(apiKey) {
const out = [];
let page = 1;
const perPage = 100;
while (true) {
const url = new URL("https://api.worksible.com/v1/companies/me/projects");
url.searchParams.set("page", String(page));
url.searchParams.set("perPage", String(perPage));
const res = await fetch(url, { headers: { "X-API-Key": apiKey } });
if (!res.ok) throw new Error(`Page ${page} failed: ${res.status}`);
const { items, total } = await res.json();
out.push(...items);
if (out.length >= total) return out;
page++;
}
}X-RateLimit-Remaining-Minute. When it gets close to zero, sleep for the value of Retry-After rather than retrying in a tight loop. See rate limits.4 · Build a real script
Below is a ~30-line reconciliation job that lists every paid invoice from the last calendar month and writes them to a CSV. Drop it in a cron and it's a working integration.
import { writeFileSync } from "node:fs";
const API = "https://api.worksible.com/v1";
const KEY = process.env.WORKSIBLE_API_KEY;
const now = new Date();
const from = new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString();
const to = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59).toISOString();
async function fetchAllInvoices() {
const all = [];
let page = 1;
while (true) {
const url = new URL(`${API}/companies/me/invoices`);
url.searchParams.set("status", "paid");
url.searchParams.set("page", String(page));
url.searchParams.set("perPage", "100");
const res = await fetch(url, { headers: { "X-API-Key": KEY } });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { items, total } = await res.json();
all.push(...items.filter((i) => i.paidAt >= from && i.paidAt <= to));
if (page * 100 >= total) return all;
page++;
}
}
const invoices = await fetchAllInvoices();
const csv = [
["invoiceNumber", "issuedAt", "totalCents", "currency", "recipient"].join(","),
...invoices.map((i) => [
i.invoiceNumber,
i.issueDate,
i.totalCents,
i.currency,
JSON.stringify(i.recipientLegalName ?? ""),
].join(",")),
].join("\n");
writeFileSync(`invoices-${from.slice(0, 7)}.csv`, csv);
console.log(`Wrote ${invoices.length} rows`);Where to next
- Conventions covers IDs, money, timestamps, pagination, and filtering — read it once, save yourself hours later.
- Idempotency is mandatory if your integration writes anything.
- API reference lists every endpoint with field-level documentation.