Build guide · About 40 minutes · Rated High on the map

Stop finding out a vendor's insurance lapsed after the claim

Certificates of insurance arrive as PDF attachments, get saved to a folder, and then nobody looks at them again until something happens. The expiry date was printed on the document the whole time. Reading it is the entire job, and no part of it needs a person.

Why the folder full of certificates does not protect you

A certificate is a photograph of coverage on the day it was issued. Policies run twelve months, vendors change carriers, and a roofer who was covered when you onboarded him in March is a different risk in April of the following year. Nobody sends you a notice when it lapses. The carrier notifies the vendor, and the vendor is the one party with no incentive to forward it.

The failure is not that you did not care. The failure is that the information was sitting in a PDF in a folder, and the only trigger to go read it was an event you were trying to prevent.

An uninsured vendor on your property is your general liability policy's problem, your deductible, and in a bad case your carrier asking why you let him on site.

What you need

The build

  1. Give the certificate one place to land One address, used by every vendor and every manager, for every property. Resist the urge to make one per property. Certificates name the certificate holder on the document, so the property is already in the file, and one intake point means one parser to maintain.
  2. Read each coverage line separately This is the step that people get wrong. An ACORD 25 carries general liability, automobile, umbrella and workers compensation on the same page, and they routinely have different expiry dates because they were written by different carriers on different renewal cycles. A vendor whose general liability runs to December and whose workers compensation lapsed in August is uninsured for the claim you are most likely to have.
    // one row per coverage, not one row per certificate
    {
      vendor:      "Southeast Roofing LLC",
      property:    "Oakwood",
      coverage:    "General Liability",   // GL | Auto | Umbrella | WC
      carrier:     "Travelers",
      policyNo:    "GL-4471902",
      eachOcc:     1000000,
      aggregate:   2000000,
      effective:   "2026-03-01",
      expires:     "2027-03-01",
      additionalInsured: true,
      waiverOfSubrogation: true
    }
  3. Let the earliest date drive the vendor's status A vendor is covered until the first of his coverages expires, not the last.
    function vendorStatus(rows, today) {
      const soonest = rows
        .map(r => new Date(r.expires))
        .sort((a, b) => a - b)[0];
      const days = Math.floor((soonest - today) / 86400000);
      if (days < 0)   return { state: "EXPIRED",  days };
      if (days <= 15) return { state: "CRITICAL", days };
      if (days <= 45) return { state: "WARNING",  days };
      return { state: "OK", days };
    }
  4. Warn on a ladder, not once A single reminder gets read on a day you are busy and then it is gone. Three, escalating, at 45 days, 15 days, and the morning it expires. The 45-day notice goes to the vendor. The 15-day notice goes to the vendor and to whoever assigns work. The expiry-day notice goes to you.
  5. Make the last alert do something An alert that only informs you gets ignored eventually. On expiry day the automation should also flip a flag the work order system can see, or at minimum email the property manager a one-line instruction: do not assign work to this vendor until a current certificate is on file. The point is to remove the decision from the moment somebody needs a roof patched in a hurry.

The thresholds, and why these ones

A starting table rather than a standard. Set your own and write down why you picked them.

Days to expiryWho hears about itWhy this spacing
45Vendor onlyLong enough that a renewal certificate can be produced without anybody rushing. Most agents turn one around in a week, and the vendor has three chances to forget before it matters.
15Vendor and whoever assigns workThe point where you stop trusting that it is handled. Adding the person who dispatches work is what changes behaviour.
0You, and a block on new workNo judgment call left. Either a current certificate exists or the vendor does not go on site.
Any lapse over 30 daysReview the vendorA vendor who cannot keep coverage current is telling you something about how the rest of the business is run.

Three things that will break it

One expiry date for the whole certificate. The most common build error and the most expensive one. Parse each coverage row. If your extraction returns a single date, it has almost certainly taken the first one it found, and the one that matters is the earliest.

The certificate holder is wrong. A vendor sends the certificate he already had, made out to somebody else's property or to his own company. That document does not name you as certificate holder and often does not carry the additional insured endorsement, which is the part that actually extends coverage to you. Check the holder name against your entity and flag mismatches rather than filing them as current.

A renewal arrives under a different name. Southeast Roofing LLC becomes SE Roofing and Sheet Metal after a reorganization, and your matcher files it as a new vendor with no history while the old record quietly expires. Match on a normalized name plus the policy number stem, and send anything ambiguous to a human rather than creating a second row.

If you would rather not build it

The kit is the finished version of everything above: the scenario blueprint, the ACORD field extraction, the vendor table with one row per coverage, and the thresholds as a spreadsheet you can edit. Email [email protected] if you would rather have it built into what you already run.

Get the next build guide

One email when a new guide goes up. Nothing else.

No sequence, no upsell drip. Unsubscribe link on every send.