Easy Labs
TreasuryGuides

Request a W-9

Collect a recipient's tax info ahead of 1099 issuance.

Goal

Email a US recipient a hosted W-9 form, capture their tax ID, and verify it. Once on file, the recipient is eligible for year-end 1099 issuance based on their cumulative payouts.

Prerequisites

  • An existing recipient with a valid email.
  • API key with Treasury + tax-documents enabled.

Implementation

1. Send the request

await client.requestW9({ recipient_id: "rcp_123…" });

Endpoint: POST /treasury/recipients/{id}/request-w9. The recipient receives an email with a hosted form. On submit, their tax ID is tokenized at intake — your servers never see the raw value.

2. Check status

const { data: taxInfo } = await client.getRecipientTaxInfo("rcp_123…");
// taxInfo.tax_id_type: "ssn" | "ein" | "itin" | "none"
// taxInfo.w9_status: "not_requested" | "requested" | "received" | "verified" | "expired"
// taxInfo.w9_received_at, taxInfo.w9_expires_at

Statuses progress: not_requested → requested → received → verified. The verified state means our TIN-matching service confirmed the ID against IRS records (where enabled for your account).

3. Backfill from your own form (alternative)

If you've collected the W-9 outside the hosted flow — e.g. via DocuSign or your own onboarding UI — you can post the tax-info fields directly:

await client.updateRecipientTaxInfo("rcp_123…", {
  tax_id_type: "ssn",
  tax_id: "123-45-6789",   // tokenized at intake; never stored raw
  w9_status: "received",
  w9_received_at: new Date().toISOString(),
});

Endpoint: POST /treasury/recipients/{id}/tax-info.

4. Pull the year-end report

const { data: report } = await client.getRecipientTaxReport();
// One entry per recipient with cumulative payout totals + W-9 readiness.

Endpoint: GET /treasury/recipients/tax-report. Use this to drive your 1099 issuance — every recipient over the IRS threshold whose w9_status isn't received or verified is a hole you need to chase before year-end.

Tradeoffs

  • You don't see the tax ID. The full SSN/EIN is intentionally unreadable from your application after intake. If you need to surface it to the recipient (for them to confirm their own info), build a flow that re-prompts them rather than echoing it back.
  • requested does not block payouts. A recipient with w9_status: "requested" can still receive payouts. If your policy is "no W-9, no pay," gate that yourself before calling createPayout.
  • Persons vs. businesses. tax_id_type must match recipient.type: ssn/itin for person, ein for business. Wrong combinations are rejected at submission, not at recipient creation.

On this page