Get started

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.

Available onProfessionalGlobalEnterpriseAOR

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 curl and 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.

text
wsk_live_AbCd1234_xR9kPmZ7nQ2vL5sH8wY3tF6jD1eB0aN4cV9uG7iM
One-time reveal
We hash the secret on creation. There is no recovery flow. If you lose it, revoke the key and create a new one. Keys can be edited (name, scopes, expiry) without rotation.

2 · 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
curl https://api.worksible.com/v1/companies/me \
  -H "Authorization: Bearer $WORKSIBLE_API_KEY"
node.js · fetch
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);
python · requests
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())
go · net/http
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:

json
{
  "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.

node.js · pagination
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++;
  }
}
Watch the headers
Every response carries 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.

node.js · monthly invoices to CSV
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.