Integrations & API

Connect Capstan to the rest of your stack.

Capstan is the system of record, and a system of record has to meet things that are not Capstan. This page describes every edge where that happens at the level a developer needs rather than the level a brochure prefers, including the edge that is not open yet and the two places that emit nothing at all. Nothing here is a roadmap item written in the present tense.

Real-time events, signed, to your own systems

Register your HTTPS endpoints and Capstan posts events as they happen. Each endpoint takes every event or a subset you pick, and can be edited or disabled in place. Every delivery is signed with the Standard-Webhooks HMAC scheme, so you can verify authenticity with any off-the-shelf library, and every attempt is kept in a per-endpoint delivery log. Webhooks are a core developer feature, not a priced module, and not gated behind a plan: you can be notified of everything before you pay for anything.

The event catalogue in full: eighteen types, and two places that emit none

  • Engagement activated
  • Engagement ended
  • Application stage changed
  • Application rejected
  • Offer extended
  • Offer accepted
  • Offer declined
  • Offer withdrawn
  • Candidate converted to employee
  • Claim submitted
  • Claim reimbursed
  • Asset issued
  • Asset returned
Helpdesk Three
  • Ticket created
  • Ticket resolved
  • Ticket closed
  • Review cycle completed
Integration health One
  • Test ping
Leave and expense approvals Emits nothing

Those transitions run through the generic workflow engine, which is deliberately subject-agnostic and has no clean seam to fire a domain event from. The claim itself emits; the approval of it does not.

Billing and invoices Emits nothing

There are no billing or invoice events at all. If your integration depends on one, it will not work, and you should know that now rather than in week three.

The catalogue inside the product is the live list. It is closed on purpose: an event type cannot exist without a place in the code that fires it, so there is nothing here you can subscribe to and never hear from. The ping is the one entry that belongs to no module, which is why you can fire one at your endpoint before you have bought anything.

The whole mechanism, including the operational parts

Most vendors describe webhooks in a sentence and leave you to find the rest after your first outage. Here are eleven things worth knowing before you build, in the order you will meet them.

Proving a delivery came from us

Three headers and one signature
Every delivery carries a webhook-id, a webhook-timestamp and a webhook-signature that is an HMAC-SHA256 over the exact bytes sent. Any off-the-shelf Standard Webhooks library verifies it, so you never need an allow-list of source addresses, and the timestamp tolerance means a captured request cannot be replayed at you later.
A secret per endpoint, shown once
Each endpoint has its own signing secret, so one compromised receiver does not implicate the others. It is displayed exactly once, at creation or rotation, and after that it is held only as ciphertext under the same per-tenant envelope encryption as the rest of your sensitive data. Nobody here can read it back to you.
Rotation that drops nothing
The outgoing secret moves to a previous slot for twenty-four hours and every delivery in that window is co-signed with both. The Standard Webhooks format carries a list of signatures, so a receiver part way through your rollover verifies against whichever secret it currently holds.
Admin only, and https only
Registering, editing, disabling or deleting an endpoint, rotating its secret and redelivering an event are admin actions, enforced by row-level security in the database and by a re-asserted admin check in the handler rather than by a hidden menu item. The target must be an https URL, and the identity classes with a narrow view of a workspace, external users and alumni, are denied by default.

When your endpoint is down

Five attempts, then a dead letter
Capstan posts with a ten-second timeout, from a background job rather than from the request that caused the event, so a slow endpoint of yours cannot slow the product down for the person clicking the button. A delivery is retried with backoff up to five attempts, then dead-letters rather than looping.
Every attempt, kept
Each attempt records the signed request Capstan sent and the response your endpoint returned, and whether it will be retried, so the log separates retrying from terminally failed rather than leaving you to infer it. Filter by endpoint and by status, then expand any attempt.
Ten failures and it tells you
Ten consecutive failures disable the endpoint and send your workspace admins a security-category notification, so a dead endpoint says so rather than silently swallowing events. Re-enabling clears the streak. The boundary worth knowing: while an endpoint is disabled, new events are not queued for it, so a long outage leaves a gap you close by reading through the product rather than by replaying a queue we held for you.
Redelivery without duplicates
Anything in the log can be redelivered by hand, and a redelivery records the next attempt against the same event rather than inventing a second one, which is enforced by a uniqueness constraint rather than by care. The webhook-id is stable across retries, so your receiver can deduplicate on it.

What a delivery is, and is not

Pointers, not copies
An event carries ids and low-cardinality fields rather than bulk personal data. Your receiver learns that something happened and to which record, then reads what it needs through the product under the permissions it already has. An endpoint is a signal, not a second copy of your directory living on someone else’s server.
An emission cannot break the write it observes
The emit call sits at the domain seam, runs through the service role, and swallows its own failures. A webhook that cannot be written never rolls back the business action that produced it, so an offer is accepted and an asset is issued whether or not the notice of it lands. Integration observes the record; it is never a condition on it.
A guide inside the product
A standalone, print-ready integration guide lives in the app, covering the envelope, signature verification, retries and the live event catalogue, with a save-as-PDF action. It is the document you hand the engineer who is actually going to build the receiver.

Not open yet

The API, stated honestly

Capstan's API is the same one the product runs on, and it is documented: the OpenAPI description is generated from the code and regenerated on every build, with the build failing on any difference, so the reference cannot drift away from the routes. What is not built is the part you need in order to use it from your own systems: API keys or personal access tokens, and a developer page to manage them. Every call today is authenticated with a signed-in session, which is right for the application and wrong for your scripts.

Until keys ship, webhooks are the real integration surface, and they are core and ungated on every plan. When keys ship they will be announced here with the plan they land on, and not before. This is written into the contract rather than only into marketing copy: clause 8.3.1 of the terms states that there is no public API surface today, and clause 8.3 states the versioning and deprecation policy that binds us from the day there is one.

Payroll: a clean handoff, not a black box

Capstan compiles every payroll input it already holds, attendance, leave, overtime, compensation, expenses, prorations, into a clean, documented export in open formats, then files your partner's computed results back as payslips and records. It is partner-neutral by design: any payroll partner that can read a file can consume it, so there is no allow-list and no certified-partner programme. Capstan holds no statutory rate, slab or formula and computes no gross-to-net, that is the partner's job, permanently. See the Payroll module.

Jurisdiction rules arrive as data, not as a deploy

Contractor withholding rules, tax-line templates and the document requirements that go with them ship as country content packs: versioned data, published on their own schedule, rather than conditional branches buried in code. A rate change is a new pack version, not a release. Nothing about your workspace has to be redeployed for a jurisdiction to change its mind.

Your workspace adopts a pack per jurisdiction and chooses how it tracks versions: follow the newest published version automatically, or pin one and hold it until you decide otherwise. Adoption is a considered choice with a confirmation rather than a one-word toggle, and it is reversible: withdrawing stops future suggestions for that jurisdiction and leaves tax lines already on existing invoices untouched.

At invoice time the adopted pack generates suggested tax lines, matched to the engagement's jurisdiction and to the threshold band the line falls into, and stamps each generated line with the pack version that produced it. So the question an auditor asks, which tenant ran which pack version for which period, is answered line by line rather than reconstructed. The suggested lines are ordinary editable draft rows: re-running the suggestion is idempotent and leaves anything entered by hand alone. The pack is the default; the accountant is the authority.

The honest limits. One pack is authored today, for India, covering contractor withholding and GST. Its seeded rates and thresholds are indicative: the machinery is production-ready and the content has to pass a jurisdiction compliance review before any live use, which is a launch gate rather than a code change. And this is contractor invoicing, not payroll. Capstan computes no statutory payroll anywhere, under any pack. See Contractor Management.

The installed app, and the one thing it does offline

Capstan installs to a phone home screen as a progressive web app, on every plan. It updates when the product updates, with no store review queue in between, and the install prompt only appears on touch devices where installing actually pays off. On an iPhone, where the browser offers no such prompt to us, we show nothing rather than faking one: Safari's own Add to Home Screen is the real path there, and pretending otherwise would only mislead.

Self-service on a phone: the punch is the one thing that keeps working with no network
Self-service on a phone: the punch is the one thing that keeps working with no network

Held on the device

  • Your last-known punch state for today, so you can see whether you are checked in.
  • The punches you have made that have not reached the server yet, queued on the device because they are writes waiting to be replayed rather than pages waiting to be re-read.

Deliberately not held

  • Leave and comp-off balances.
  • Documents of any kind.
  • Approvals, yours or anyone else’s.

That data is authorisation-sensitive, and a stale balance or a stale approval list read on a train misleads in a way that matters: someone acts on a number that has since changed, or believes an approval still pends that was actioned yesterday. Staleness of that kind is worse than absence, so we show nothing rather than something old. It is a correctness decision, not a storage budget.

A queued punch cannot become two punches

Each one carries an idempotency key generated on the device, so a replayed retry returns the same server record rather than a second punch. The in or out direction is inferred locally from your last-known state, shown to you, and confirmed before the punch is queued, because the server’s own inference cannot run when the server is unreachable.

Replay is careful about failure

The queue replays in the order the punches happened, when the app opens, when the network returns, or when a session comes back after signing in, and it refuses to replay without a valid session rather than dropping anything. A transient failure stops the batch and keeps the rest queued; a punch the business rules genuinely refuse is dropped with its reason recorded and routed to regularisation, never silently discarded.

A device clock is not taken on trust

An offline gap is legitimately long, so a punch time in the past is accepted without question. A time in the server’s future beyond a small tolerance is clamped to the server’s own clock and flagged, which surfaces the day as an anomaly through the existing regularisation path. A fabricated future time is an integrity problem, and a flag is the honest response to it.

What is not here, said plainly

Five things this page will not dress up as features, each with what exists in place of it.

  • No connector marketplace, and no catalogue of vendor integrations being built quietly in the background. Webhooks are how you integrate today, and if a marketplace ever ships it will be listed here with what it actually connects to.
  • No SSO and no SCIM, so sign-in is not federated to your identity provider and provisioning is not driven from your directory.
  • No API keys or personal access tokens, so a script of yours has no way to authenticate. Every call today rides a signed-in browser session, which is right for the application and wrong for a script.
  • No inbound receiver, because webhooks run one way only. The write paths are file-shaped and deliberate, and the switching page describes each one and its limits.
  • No Slack, Teams, WhatsApp or mobile push, notifications reach people by email and inside the app, and by nothing else today, so there is no chat channel to wire up.

What does exist is signed webhooks, the payroll export, the file imports described on the switching page, and a self-serve export of your whole workspace as a plain tar of documented JSON and CSV. All real, all documented, and enough to build against. When the missing pieces ship, they will appear here with what they actually do.

Integration questions, answered

Do you have a marketplace of pre-built connectors?

No, and there is no catalogue of vendor integrations being built quietly in the background either. The way you integrate with Capstan today is to build against outbound signed webhooks, which are part of the core on every plan. A connector marketplace is a deferred idea rather than a dated commitment, so it is not on this page as a promise; if it ships, it will be listed here with what it actually connects to, and not before.

Are webhooks a paid add-on?

No. Outbound webhooks are a core developer feature on every plan, including the free one. They are not a priced module, they are not behind a plan gate, and enabling a paid module is not a precondition for using them: you can be notified of everything before you pay for anything. Endpoint registration, the signing secret, the per-attempt delivery log, manual redelivery and the in-product integration guide are all part of that same core surface.

How do I trust a webhook actually came from Capstan?

Every delivery is signed with the Standard Webhooks HMAC-SHA256 scheme. Each request carries three headers: a webhook-id, a webhook-timestamp and a webhook-signature over the exact bytes delivered, with a timestamp tolerance so a captured request cannot be replayed at you later. Verify it with any off-the-shelf Standard Webhooks library, which means you never have to trust the network or an allow-list of source addresses. The same signing scheme is what Capstan itself uses to verify inbound calls from its payment provider, so it is not a second-class path built only for customers.

Which events can I subscribe to?

A closed catalogue, grouped by module, in which every entry fires from a real place in the code. It covers contractor engagements activated and ended; recruitment events, meaning an application changing stage or being rejected, an offer extended, accepted, declined or withdrawn, and a candidate converted into an employee; expense claims submitted and reimbursed; assets issued and returned; helpdesk tickets created, resolved and closed; a review cycle completed; and a test ping you can fire on demand. Two absences are worth knowing before you design against it. Leave and expense approvals emit no event, because those transitions run through the generic workflow engine and have no clean seam to fire from. And there are no billing or invoice events at all. The catalogue inside the product is the live list, and it is closed on purpose: there are no dead event types you can subscribe to and never hear from.

Can I call the API from my own scripts?

Not yet, and the distinction matters more than a yes or no. Capstan runs on its own HTTP API, and that API is documented: the OpenAPI description is generated from the code itself and regenerated on every build, with the build failing on any difference, so the reference cannot drift away from the routes. What does not exist is the part you would need in order to call it from outside the product: API keys or personal access tokens, and a developer page to manage them. Every call today is authenticated with a signed-in browser session, which is right for the application and wrong for a script. Until keys ship, webhooks are the real integration surface and they are core and ungated. When keys ship we will say so here, and say which plan they land on.

Can I push data into Capstan from another system?

Not over the network, and not by webhook. Webhooks run one way only: Capstan posts to your endpoints, and there is no inbound receiver for you to post events at. Without API keys there is also no authenticated way for one of your scripts to write. What does exist is file-shaped and deliberate: a spreadsheet import for people, a CSV import for a holiday calendar, a CSV import for the asset register, a per-person bulk document upload, and the payroll partner export and its matching import of the partner’s computed results. Those are the write paths today, and the switching page describes each one and its limits.

What are the rate limits?

There is no published per-customer API rate limit, because there is no public API surface to put one on yet. What is limited today is a specific set of expensive or abuse-prone operations inside the product: signup, which has its own per-address and per-email limiter, verification resend, token confirmation, starting a full tenant export, and running a report. Those limits are configuration rather than a published contract, and interactive sign-in is limited by the identity provider that issues the session rather than by us. Outbound webhooks have no throttle in the other direction: Capstan posts an event when the event happens, so your endpoint should accept bursts and return quickly rather than doing work on the request. When a public API with keys ships, its limits publish alongside it.

How is any of this versioned?

The API is versioned in the path: every route lives under a version prefix, and the generated OpenAPI description is regenerated on every build with the build failing on any difference, so what is documented is what is deployed. Clause 8.3 of the terms of service carries the standing policy that goes with that, meaning a breaking change ships only as a new version, a deprecation carries at least six months of notice, and a version is supported for at least twelve months after its successor. Clause 8.3.1 then states the limit of what that policy currently governs, which is that there is no public API surface today, so there is nothing yet under contract to deprecate. The policy binds us from the day keys ship. The webhook event catalogue is a closed vocabulary held in the database, so adding an event type is a migration plus an emit call at a real seam rather than a silent change, and an event type cannot exist without somewhere that fires it.

What happens if my endpoint is down for a long time?

Each delivery is retried with backoff up to five attempts, and every attempt is recorded against the endpoint with the request that was sent and the response that came back, so a failure is visible rather than inferred. Each attempt also records whether it will be retried, so the log distinguishes retrying from terminally failed, and a delivery that exhausts its attempts dead-letters rather than looping. If an endpoint fails ten times in a row, Capstan disables it and sends your workspace admins a security-category notification, so a dead endpoint tells you rather than silently swallowing events. Re-enabling the endpoint clears the failure streak. Anything in the delivery log can be redelivered by hand, and a redelivery records the next attempt against the same event rather than inventing a duplicate one. The boundary worth knowing: while an endpoint is disabled, new events are not queued for it, so a long outage leaves a gap you close by reading the data through the product rather than by replaying a queue we held for you. Every delivery carries a webhook-id that is stable across retries, so your receiver can deduplicate on it.

How do webhook signing secrets work, and can I rotate one safely?

Yes, without dropping a delivery. A signing secret is per endpoint, not per workspace, and it is shown to you exactly once, when the endpoint is created or the secret is rotated. After that it is stored only as ciphertext under the same per-tenant envelope encryption as the rest of your sensitive data, so the database never holds it in plaintext and nobody here can read it back to you. Rotation is zero-downtime: the outgoing secret moves to a previous slot for twenty-four hours, and every delivery in that window is signed with both the new secret and the old one at once. The Standard Webhooks format carries a list of signatures, so a receiver part way through your rollover verifies against whichever secret it currently holds, and no delivery is lost while you deploy.

Is there a sandbox or test workspace?

No, and we are not going to imply one. There is no test workspace you can provision alongside your live one, and nothing is built towards one today. What you do have for testing an integration is narrower and real: a test ping action that delivers a signed event to your endpoint on demand, a per-attempt delivery log showing the exact signed request and your endpoint’s response, filterable by endpoint and by status, a redelivery action, and a standalone, printable integration guide inside the product covering the envelope, signature verification and retries. The sensible sequence is to verify your signature checking against a ping before you rely on a real event.

Can a webhook failure break something inside Capstan?

No, and that is a structural property rather than a promise to be careful. An event is emitted at the domain seam through the service role, and the emit call swallows its own failures, so a webhook that cannot be written never rolls back the business action that produced it. An offer is accepted, an asset is issued and a ticket is resolved whether or not the notification of it succeeds. The same separation runs the other way: a delivery is posted with a ten-second timeout by a background job on the queue, never on the request that caused the event, so a slow endpoint of yours cannot slow the product down for the person clicking the button.

Who can register an endpoint, and what ends up in a payload?

Only a workspace admin, and less than you might expect. Endpoint registration, editing, disabling, deletion, secret rotation and redelivery are admin-only, enforced by row-level security in the database and by a re-asserted admin check in the handler rather than by hiding a menu item. The target has to be an https:// URL. The identity classes with a narrow view of a workspace, external users and alumni, are denied by default, so they cannot register a destination for your events. Payloads carry ids and low-cardinality fields rather than bulk personal data, which means an endpoint is a signal to go and read the record, not a second copy of your directory sitting on someone else’s server.

Which payroll partners do you support?

Any partner that can read a file. Capstan compiles the payroll inputs it already holds, attendance, leave, overtime, compensation, expenses and prorations, into a documented, partner-neutral export in CSV and JSON on a versioned schema, and files the partner’s computed results back as payslips and records against each person. There is no partner allow-list and no certified-partner programme, because the export is a format rather than an integration with a named vendor. Capstan computes no statutory payroll itself: it holds no rate, slab, bracket or formula anywhere, and that absence is permanent and structural rather than a gap waiting to be filled.

Do contractor tax rules update without waiting for a software release?

Yes, for the jurisdictions we have authored. Withholding rules, tax-line templates and document requirements ship as versioned country content packs, which are data rather than code branches, so a rate change is a new published pack version rather than a deployment. Your workspace adopts a pack per jurisdiction and chooses whether to follow the newest published version automatically or pin a specific one, and adoption is reversible. At invoice time the adopted pack suggests editable tax lines matched to the engagement’s jurisdiction, and every generated line records which pack version produced it. The suggestion is a default your accountant edits before submitting, never a calculation you cannot touch. The honest limit: one pack is authored today, for India, covering contractor withholding and GST, and its seeded rates and thresholds are indicative and must pass a jurisdiction compliance review before live use. This is contractor invoicing, not payroll, which Capstan never computes.

Does attendance work when there is no signal?

Punching does, and almost nothing else does, which is the point. Capstan installs to a phone home screen as a progressive web app, and offline it holds exactly two things: your last-known punch state for today, and the punches you have made that have not reached the server yet. A punch made offline is queued on the device with a client-generated idempotency key, so a replay can never become a double punch, and with its in or out direction inferred locally from your last-known state, shown to you and confirmed before it is queued. The queue replays in time order when the app reopens, when the network returns, or when a session comes back after signing in, and it refuses to replay without a valid session rather than dropping anything. Balances, documents and approvals are deliberately not available offline, because a stale approval list or a stale balance is misleading in a way that matters, and staleness of that kind is worse than absence.

Signed webhooks are core, on every plan, including the free one.