Skip to main content
HoundDog API Documentation

W-9s

W-9 tax forms your account collects from parties: requested by email, signed online on HoundDog's form or uploaded as a file.

Operation Scope
GET /w9s read
GET /w9s/:id read
POST /w9s read_write
POST /parties/:id/w9 read_write
GET /w9s/:id/download read + the w9 grant
DELETE /w9s/:id read_write

Rules for every W-9 route

  • Reads always work; writes need W-9 collection on. GET routes answer whether or not the account collects W-9s (history stays visible). Every write, POST /parties/:id/w9 included, is 403 feature_disabled when W-9 collection is off.
  • Order of checks on a write: the feature gate (403), then ids in the path (404), then the body (422 validation_failed), then conflicts (409 conflict).
  • Individuals: the W-9s of your individuals are 404 on every /w9s/:id route and never listed; their ids as party_id in a body or query are 422 naming party_id.
  • updated_since sees record edits only. The email counters are written without touching updated_at.

Tax identification numbers

A TIN is sensitive. By default the entity shows only tin_type (ssn or ein) and tin_last4.

You want You need
tin_type, tin_last4 Any key
The full tin in JSON Both the account setting "Allow tax IDs through the API" (turned on by HoundDog support on your account's written request) and the key's w9 grant
GET /w9s/:id/download (the PDF, which contains the TIN) The key's w9 grant

The w9 grant is a per-key switch, set on the API Credentials page by a user with the Manage API Access permission; every change to it records an API Key Updated event. It works with either scope. TINs are returned zero-padded to nine digits, as a string. They never appear in events, webhook payloads, JSON or text exports, logs or error messages (the dashboard's W-9 ZIP export contains the PDFs, which carry the TIN). Store them as you would any tax identifier: encrypted, and only where you need them.

The W-9 entity

{
  "id": 4410,
  "party_id": 191537,
  "status": "complete",
  "source": "signed_online",
  "signed_at": "2026-10-03T18:22:00Z",
  "form_year": 2024,
  "name": "Dana Ruiz",
  "business_name": "Acme Electric LLC",
  "tax_classification": "Limited liability company",
  "tax_classification_llc": "S corporation",
  "tax_classification_other": null,
  "exempt_payee_code": null,
  "exemption_fatca_code": null,
  "foreign_partners": false,
  "address": "12 Main St",
  "address2": "Austin, TX 78701",
  "certification_agreed": true,
  "tin_type": "ein",
  "tin_last4": "6789",
  "email_request_count": 1,
  "email_last_sent": "2026-10-01T15:02:30Z",
  "requested_by": { "user_id": 301 },
  "form_url": "https://app.hounddog.com/forms/w9/3d9a...",
  "created_at": "2026-10-01T15:02:11Z",
  "updated_at": "2026-10-03T18:22:00Z",
  "links": { "self": "/api/v2/w9s/4410", "download": "/api/v2/w9s/4410/download", "party": "/api/v2/parties/191537" }
}
Field Meaning
status pending (requested, still outstanding) or complete
source signed_online (signed on HoundDog's form), uploaded (a file: uploaded by the party on the public form, by your staff or an API key, synced from JobTread, or converted from a certificate upload; the data does not tell these apart), or null while pending and for a complete, not-uploaded W-9 with no signature date
signed_at When it was signed or uploaded; null for a W-9 converted from a certificate upload
form_year 2018 or 2024, the IRS form revision, for W-9s signed online; null otherwise
tax_classification (+ tax_classification_llc, tax_classification_other when set), exempt_payee_code, exemption_fatca_code, foreign_partners, name, business_name, address, address2, certification_agreed The form's answers. exemption_fatca_code is stored as a number, so letter codes were never kept
tin_type, tin_last4, tin See above; tin is present only under both conditions. A stored number longer than nine digits is not a valid TIN: tin is then null and tin_last4 shows its last four digits
email_request_count, email_last_sent The W-9's own reminder cadence
requested_by {"user_id": n} or {"api_key_id": n}; null when the app created the W-9 (collect-on-create, the multi-factor bootstrap, a JobTread sync) and when the W-9 was created by an upload (by your staff or an API key). A W-9 converted from a certificate upload names the certificate's uploader
links self, party, and download once the W-9 is complete
form_url The W-9 request page the party is emailed, where it signs the form online or uploads a file while the W-9 is pending; the same link as the party's W-9 entries in outreach. A bearer link: anyone holding it can submit a W-9 for the party, so treat it like a password, share it only with the party, and never log it

An uploaded W-9 has every structured field null, tin_type and tin_last4 included: the file is the record. Never served: the signer's IP address, the W-9's link code on its own (it is part of form_url), internal sync ids.

GET /w9s

Filters: party_id, status (pending or complete), updated_since, plus page, per_page.

GET /w9s/:id

One W-9.

POST /w9s

Requests a W-9 from a party and sends the request. Body: party_id (required), replace (default false).

  • 409 conflict when the party already has a pending W-9, naming the newest pending one in the error's conflict member ({"resource": "w9", "id": n}). With replace: true, every pending W-9 of the party is deleted first (W9 Removed for each; their reminders stop and their links stop working) and the new one is created in the same transaction.
  • 422 validation_failed when the party is archived.
  • Under multi-factor compliance, also 422 validation_failed when the party's W-9 requirement is already satisfied (a profile that does not require a W-9, or an active universal override, or one for the W-9 factor, counts as satisfied).
  • A complete W-9 on file is not a refusal: on an account without multi-factor compliance a new request is created, as the dashboard's "Request W-9" does.

What goes out: the request's initial contact is sent as vendor outreach. With vendor email off on the account nothing is emailed, but an SMS still goes out when SMS is on, and it counts in email_request_count. A paused party is accepted. On a multi-factor compliance party with two or more required factors, the W-9's own initial contact (email and SMS) is replaced by the combined compliance email, unless the W-9 is routed to another contact person, who then gets the W-9's own initial. Any write that creates a record on such a party can email the main contact at once (the combined email), so never assume a W-9 request is a silent change. The combined email is not sent while the party's combined cadence is exhausted with the W-9 already listed, for a routed W-9, for a paused party, or on an account that disables requests. The W-9's own request still goes out to a paused party (where it is not replaced by the combined email) and to a routed W-9's contact.

Response 201 with the entity. Event: W9 Request Sent (when a send happens).

POST /parties/:id/w9

Uploads a completed W-9 file for the party, as the dashboard's "Upload W-9". multipart/form-data with file.

  • Completes the party's newest pending W-9 (or creates a complete one), and deletes its other pending W-9s (W9 Removed for each). Event: W9 Uploaded.
  • signed_at is the upload time; it cannot be backdated.
  • 422 for an archived party, and 422 validation_failed naming file for an empty file.

Side effects: when the account's JobTread integration copies W-9s and the party is linked to JobTread, the W-9 is pushed there. On a multi-factor compliance account the party's compliance is recomputed a few seconds later, which can change its status and fire the compliance events.

Response 201 with the W-9 entity.

GET /w9s/:id/download

Streams the PDF (generated from the answers for a W-9 signed online, the stored file for an uploaded one) with Content-Disposition: attachment. Requires the key's w9 grant (403 forbidden without it). 404 while pending, and 404 ("no document to download") for a complete W-9 with nothing to render: one not uploaded and with no signature date, or an uploaded one whose stored file is missing.

DELETE /w9s/:id

Deletes the W-9 and its stored file, then recomputes the party's compliance. Event: W9 Removed. Response 204.

Deleting can send email in the same request. On a multi-factor compliance party whose profile requires a W-9 and who is left with no pending or complete W-9 (and no active override waiving it), the recompute creates a new pending W-9 at once (not for an archived or paused party, or on an account that disables requests). That new W-9 contacts the party at once on a single-factor party or when the W-9 is routed to another contact. On a party with two or more required factors the same recompute can send the combined compliance email to the main contact: when the cadence counters were at zero (for example the party was compliant) or when the new W-9 restarts the cadence. So deleting a compliant party's W-9 can email the party in the same request.

There is no W-9 reminder endpoint: W-9 reminders follow the account's follow-up schedule.