Quickstart

A human approval,
in about five minutes.

One call puts a decision in front of a person and waits for it. The first example below needs no account — run it now, then come back for a key when you want a real approver.

Step one

Install

npm install @deliverd/sdk

TypeScript-first, and no dependencies of its own.

Step two

Run it with nobody in the loop

Development mode needs no key and makes no network calls. It is the same client and the same code path as production — retries, error handling, the wait — with an imaginary approver on the other end, so the integration you write here is the one that ships.

import { Deliverd } from "@deliverd/sdk";

const deliverd = new Deliverd({ mode: "development" });

const decision = await deliverd.approve({
  title: "Deploy to production",
  description: "Ship 14 commits to the live site.",
  risk: "high",
});

console.log(decision.approved ? "shipping" : `held: ${decision.note}`);

The imaginary approver says yes by default. Set DELIVERD_DEV_OUTCOME to rejected and the same code takes the other branch — a refusal is an answer, not an error.

timeout and question are the two that throw: the first because you stopped waiting, the second because an approver asked something and nothing answered. Both need a try around the call — and question wants an onQuestion handler, which is the whole point of it.

Step three

Now with a real person

Sign up, name an organisation, and the next page hands you a key. Put it in the environment rather than the code:

export DELIVERD_API_KEY="dlv_your_key_here"

With that set, the client needs no arguments at all:

import { deliverd } from "@deliverd/sdk";

const decision = await deliverd.approve({ title: "Deploy to production" });

Or pass it explicitly, and name who should decide — anyone else in your organisation. Leave approvers out and it goes to the owners and admins.

import { Deliverd } from "@deliverd/sdk";

const deliverd = new Deliverd({ apiKey: "dlv_your_key_here" });

const decision = await deliverd.approve({
  title: "Deploy to production",
  approvers: ["you@example.com"],
});

if (decision.approved) {
  await deploy();
}

What you just built

Between the call and the answer

None of this is yours to build — that is the whole of what the one call stands in for.

1

Your call blocks

approve() resolves when somebody decides — including when they say no, because a refusal is an answer. It throws only if the wait itself failed.

2

They get an email and an inbox item

Addressed to the people you named, or to your organisation's owners and admins if you named nobody. A request addressed to you cannot be muted.

3

They open a page that works on a phone

What is being asked, who asked, how risky they said it is, and what to read first. They approve, reject with a reason, or ask you a question.

4

You get the decision back

As the resolved value, as a signed webhook, or by polling — whichever suits the process you are writing.

Retries are safe, so you can write the obvious code.

The SDK sends an idempotency key with every call and keeps it across its own retries, so a request that arrives twice is only ever done once. Nobody gets asked to authorise the same thing because a connection dropped.