UntilPaid

Guides Getting paid

How to lock access until a client pays

Pausing a staging site or client portal until an invoice is paid is the one step a client can’t scroll past. Done fairly, it’s agreed in the contract, announced with a date, and undone the moment they pay.

When pausing work is fair

Locking a client out is a consequence, so ask the same question you’d ask of any other: would a reasonable client see it coming? Three things make the answer yes.

  • It’s in the agreement. They signed something that says unpaid invoices pause the work.
  • They were told the date. The final notice names the day access pauses, a few days ahead, so they can pay first.
  • It’s undone straight away. The moment they pay, everything comes back, without them having to ask.

And one rule on top: if they dispute the work, stop and talk to them first.

Put it in the contract

Add a clause like this to your next proposal or terms. It’s short on purpose. A rule a client can read in ten seconds is easier to agree to.

Clause Suspension for non-payment

If an invoice is more than 14 days overdue, Your Agency Studio may suspend access to staging sites, client portals and undelivered work, after giving at least 3 days’ written notice of the date. Access is restored as soon as the invoice is paid in full. Suspension does not change the amount owed or the dates in this agreement.

A starting point, not legal advice. Have it checked for your country before you rely on it, especially if any of your clients are consumers rather than businesses.

What to lock, and what not to

Lock what you’re still providing, not what they already own.

  • Good candidates: a staging or preview site, a client portal or dashboard you host, downloads of files not yet handed over, the next sprint or round of revisions.
  • Think hard first: a live production site. Taking it down hurts their customers as well as them, and it’s much harder to defend as proportionate.
  • Don’t: hold back work they’ve already paid for, or anything the contract says they own.

Doing it by hand

By hand, it takes four steps.

  1. On day 10, send the final notice with the date access pauses (our final notice template).
  2. On that date, check the invoice is still unpaid, then switch on maintenance mode, revoke the portal login or remove the download links.
  3. Send the “work is paused” email with the pay link.
  4. Watch for the payment, and restore everything the same day it lands.

Step 4 is where doing it by hand goes wrong. A client who paid on Friday evening and is still locked out on Monday has a real complaint.

Doing it with a webhook

This is the last step of UntilPaid’s ladder. When the date in the final notice passes and Stripe still shows the invoice unpaid, UntilPaid sends a signed request to an address you choose. When Stripe marks it paid, a second request unlocks it.

Day 14 · Lock accessPOST /your-endpoint
{
  "event": "invoice.access_revoke",
  "stripeInvoiceId": "in_1Qk8aZ…",
  "stripeCustomerId": "cus_R2x7…",
  "amountDue": 480000,
  "currency": "usd"
}
Paid · UnlockPOST /your-endpoint
{
  "event": "invoice.access_restore",
  "stripeInvoiceId": "in_1Qk8aZ…",
  "stripeCustomerId": "cus_R2x7…",
  "reason": "paid"
}

Both are trimmed, and the real requests carry a few more fields. Amounts are in cents, so 480000 is $4,800.00. Your endpoint does the actual locking, so it can be anything you control: a staging site’s password, a portal flag, a WordPress plugin.

Verifying it’s really us

Each request carries an x-untilpaid-signature-v1 header: t=<unix time>,v1=<signature>. The signature is an HMAC-SHA256 of the time, a dot and the raw body, keyed with your signing secret, in hex. Reject anything older than five minutes.

Node.js · verify the signature
import crypto from "node:crypto";

export function verifyUntilPaid(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map(p => p.trim().split("=", 2)));
  const t = parts.t || "", v1 = parts.v1 || "";
  if (!/^\d+$/.test(t) || !/^[0-9a-f]{64}$/i.test(v1)) return false;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(v1, "hex"), Buffer.from(expected, "hex"));
}

The dashboard has ready-to-use receivers for Node.js, Python and PHP, a WordPress plugin, and a button that sends a test lock and unlock to your endpoint.

The safeguards built in

  • Per client, not account-wide. You switch Lock access on for each client, confirming their contract allows it.
  • Never out of the blue. The final notice names the date, at least three days ahead.
  • Disputes stop it. A disputed invoice pauses chasing until you’ve looked.
  • Unlocks on its own. When the invoice is paid or voided, or you unlock it yourself, the unlock request goes out.

Lock access is on the Pro plan ($79 a month) and Agency. Starter sends the email ladder without it. The 14-day trial includes it.

More on getting paid

Payment reminder email templatesFour plain-text emails, from a friendly nudge to a final notice. What to do when a client won’t payFrom checking the facts to pausing work and last-resort options. How to chase overdue invoicesA ladder you can run every time, so chasing stops being personal. Stripe payment reminders vs. escalationWhat Stripe’s built-in reminders do, and when they’re enough.