Disclosure: I made PhilForms. I drafted this post with an AI assistant (Claude) and ran every request below myself against the live API. This post uses its public API. Not affiliated with BIR, SSS, PhilHealth, Pag-IBIG or any government agency.
PhilForms fills Philippine government forms from data: you send values keyed by parameter name, and they land in the form's printed boxes. The fill call itself needs an API key (the docs are at philforms.com/developers). I am not showing a fill response here, because I did not run one for this post. What needs no key, and what I did run, is the part that saves you the most time: finding the form, reading its contract, and dry-running your payload.
Everything below is a real response from https://philforms.com, run on 2026-11-01 with made-up sample data. Long arrays are trimmed where I say so.
1. Find the form
curl -s "https://philforms.com/api/v1/engine/forms?q=2307"
The form array in the response:
[{"id":"bir-2307","name":"BIR 2307 · Certificate of Creditable Tax Withheld at Source","agency":"bir","parameter_count":31,"page_count":2}]
2. Read the form's contract
curl -s https://philforms.com/api/v1/engine/forms/bir-2307
Each entry in parameter has a name, type, format and an example. This is the one for the payee's TIN:
{"name":"payee_tin","label":"Taxpayer Identification Number (TIN)","type":"tin","id_kind":"tin","expected":"a 9-digit TIN, optionally followed by a 3, 4 or 5-digit branch code (12, 13 or 14 digits in all; NNN-NNN-NNN-NNNNN)","is_core":true,"is_required":true,"field_number":"2","section":"Part I – Payee Information","format":"NNN-NNN-NNN-NNNNN","example":"123-456-789-00000","cell_count":14}
3. Dry-run a payload
POST /api/v1/engine/check/{id} takes the same body a fill would, and returns the issues a fill would raise. No key, no PDF. My sample body (all names, addresses and numbers invented):
{"period_from":"01/01/2026","period_to":"03/31/2026","payee_tin":"123-456-789-00000","payee_name":"DELA CRUZ, JUAN SANTOS","payee_registered_address":"123 Sample St., Brgy. Sample, Pasig City","payee_zip_code":"1605","payor_tin":"987-654-321-00000","payor_name":"SAMPLE TRADING CORP.","payor_registered_address":"456 Example Ave., Makati City","payor_zip_code":"1200"}
curl -s -X POST https://philforms.com/api/v1/engine/check/bir-2307 \
-H 'content-type: application/json' -d @body.json
Response (the missing list, which names every blank parameter such as the income table and signatures, is trimmed):
{"form_id":"bir-2307","is_valid":true,"issue":[],"matched_count":10,"unknown_key":[],"missing_core":[],"core_field":["payee_tin","payor_tin","payee_name","payee_registered_address"],"warning":[],"copy_count":1,"annex_count":0,"can_fit_row":false}
4. What a bad value looks like
The part I like as a developer: a value the form cannot hold is refused with a reason, not quietly "cleaned". Here is a TIN with a letter in it and a 2-digit ZIP:
curl -s -X POST https://philforms.com/api/v1/engine/check/bir-2307 \
-H 'content-type: application/json' \
-d '{"payee_tin":"123a44","payee_zip_code":"16"}'
Trimmed to the useful keys:
{"form_id":"bir-2307","is_valid":false,"error":"A fill would refuse 2 values — see issue","code":"invalid_value","issue":[{"field":"payee_tin","code":"has_letter","message":"\"123a44\" has letters — expected a 9-digit TIN, optionally followed by a 3, 4 or 5-digit branch code (12, 13 or 14 digits in all; NNN-NNN-NNN-NNNNN)"},{"field":"payee_zip_code","code":"wrong_length","message":"\"16\" has 2 digits — expected a 4-digit ZIP code"}],"missing_core":["payee_name","payee_registered_address","payor_tin"]}
Branch on code (invalid_value, has_letter, wrong_length), not on the message sentence.
Where this fits
Put the check in front of your own form or import step, show the user the issue messages, and call fill only when is_valid is true. The result is a PDF that is ready to print, sign and submit yourself. Tax questions go to your accountant or the agency, not to an API.
There is also a hosted MCP server at https://philforms.com/mcp if you would rather let an AI client do these calls, with a client repo at github.com/philforms/philforms-mcp. The OpenAPI spec is at https://philforms.com/api/v1/openapi.json.
Not affiliated with BIR, SSS, PhilHealth, Pag-IBIG or any government agency.
Top comments (0)