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.
GETroutes answer whether or not the account collects W-9s (history stays visible). Every write,POST /parties/:id/w9included, is 403feature_disabledwhen 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 (409conflict). - Individuals: the W-9s of your individuals are 404 on every
/w9s/:idroute and never listed; their ids asparty_idin a body or query are 422 namingparty_id. updated_sincesees record edits only. The email counters are written without touchingupdated_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
conflictwhen the party already has a pending W-9, naming the newest pending one in the error'sconflictmember ({"resource": "w9", "id": n}). Withreplace: true, every pending W-9 of the party is deleted first (W9 Removedfor each; their reminders stop and their links stop working) and the new one is created in the same transaction. - 422
validation_failedwhen the party is archived. - Under multi-factor compliance, also 422
validation_failedwhen 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 Removedfor each). Event:W9 Uploaded. signed_atis the upload time; it cannot be backdated.- 422 for an archived party, and 422
validation_failednamingfilefor 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.