Tax transcript
Lender records, shaped like apis.tax's. A lender reads a return the brand filed, on the §7216 consent that names it, or orders a transcript on the taxpayer's Form 4506-C under the brand's own credential on a 120-day clock. Each field cites its form, year and line, and the acts that belong to people are refused.
tax's wordsreturn is a jobtaxpayer is a subjectreview is a taskpreparer is a member
A lender needs a borrower's figures, each sourced, and the income figured the way underwriters figure it. When apis.tax prepared the return and the §7216 consent names the lender, the lender reads the filing itself, with no wait. Otherwise the taxpayer signs a Form 4506-C and the transcript is ordered under the brand's participant status.
$ cat services/transcript.ts
import { Service } from 'services.dev'
import { selfEmployedIncome } from '../lib/tax.ts'
import rules from '../rules.ts'
// Lender records, on the taxpayer's consent, every field sourced. A lender orders a taxpayer's years from one of two
// sources. `filed`: the return apis.tax filed, read under the §7216 consent the taxpayer signed when it was prepared and
// that names this lender; each field cites that filing's receipt, form, year and line, with no wait and no new 4506-C.
// `ives`: the taxpayer signs a Form 4506-C, and the transcript is ordered under the brand's own participant status, on a
// clock that holds it to 120 days after the signature. Either way the AI reads the fields and the income is figured the
// Form 1084 way. It is published before it sells, answering not_live with its waitlist until the brand flips it live.
// Each year is charged on its own receipt, so a not-found is never charged.
export default Service({
name: 'Tax Transcript',
description: "Lender records on the taxpayer's consent, every field sourced.",
| The lender gets | The step |
|---|---|
| A return the brand filed, on the consent that names the lender | disclose, held to the disclose row of the rules table, when source is filed |
| A transcript on the taxpayer's signed 4506-C | consent, the taxpayer's own e-signature, then order under the brand's ives credential, when source is ives |
| Received within 120 days of signing | the order clock, due 120d after consent, owned by the brand |
| Each field with its form, year and line, and a filing's receipt | fields, AI, citing the filing or the transcript |
| Self-employed or business income, the Form 1084 way | income, your selfEmployedIncome in lib/tax.ts |
| No return prepared, no taxpayer represented, no consent signed for anyone | three refused rows in rules.ts |
Nothing is charged until the order's receipt or the disclosure, so a transcript not found costs nothing.
On the taxpayer's 4506-C, the order runs against the participant program, and the income step figures the sample borrower's Schedule C:
node -e "import('./flows.ts')" # the apis.tax door at 17a25d0: 16 flows in holdings/public/apis.tax/llms.txt and holdings/public/apis.tax/data.js llms.txt#income.analysis: Schedule C income added back the Fannie Mae Form 1084 way, by year, with the trend and the 24-month average when income is stable or rising
→ tax-example › figures the sample borrower's income the Form 1084 way · lib/tax.ts figures it, and the transcript's income step runs it on the fields it reads
data.js#the sample borrower's income: 2024 $105,300 and 2025 $113,600 added back, increasing 7.88%, $9,120.83 a month on the 24-month average
→ tax-example › figures the sample borrower's income the Form 1084 wayFern & Co's consent on its 2026 return names Harbor Bank for business lending, so Harbor Bank reads the filed return, each field cited to its receipt:
→ POST /returns {"service":"transcript","taxpayer":{"name":"Fern & Co","email":"books@fern.example"},"ein":"98-7654350","tin_last4":"4350","years":[2026],"source":"filed","purpose":"business lending","max_usd":12,"key":"tr-fern"} # Harbor Bank's key
← job_4Tr1 · delivered · fields.json · income.json · $12.00 charged · at disclose: no wait, no 4506-C
disclose job_4Ta1's §7216 consent, ask_7Ha2, signed 2027-02-01 by Maya Fern as officer · names Harbor Bank, for business lending · until 2028-02-01 · rules 2026-10 · services['transcript'].disclose
fields Form 1120 · 2026 · line 28, taxable income · rcp_7Ha6 · line 31, total tax · rcp_7Ha6 · line 33, payments · rcp_7Ha6 and each payment's own receipt
A lender the consent doesn't name is refused before any work, at $0, and next asks the taxpayer for a consent that names it:
→ POST /returns {"service":"transcript","taxpayer":{"name":"Fern & Co","email":"books@fern.example"},"ein":"98-7654350","tin_last4":"4350","years":[2026],"source":"filed","purpose":"business lending","max_usd":12,"key":"tr-fern-2"} # Northline Credit's key← 403 consent_required: disclose needs the taxpayer's §7216 consent to name this lender, and job_4Ta1's names Harbor Bank alone. Ask Maya Fern for one, or order with source: 'ives' and her Form 4506-C.
cites rules 2026-10 · services['transcript'].disclose
next hand_to_person https://apis.tax/asks/new?consent=7216&for=Northline%20CreditThree rows refuse the acts that belong to people, and route two of them to the brand's own Services. Every rules row is illustrative, and the rules table shows a refusal answered:
grep -A5 "transcript: {" rules.ts transcript: {
order: { reserved: { brand: 'ives' }, position: 'Transcripts are ordered through the brand\'s participant status.' },
disclose: filing.disclose,
prepare_return: { refused: true, route: 'https://apis.tax/annual-return', position: 'A paid preparer with a PTIN prepares a return: the transcript door prepares none.' },
represent_taxpayer: { refused: true, route: 'https://apis.tax/notice-response', position: 'A CPA, enrolled agent or attorney represents a taxpayer: the transcript door represents none.' },
sign_consent: { refused: true, position: 'The taxpayer signs the consent: nothing here signs for them.' },The brand publishes the Service before it sells, at status: 'roadmap', while its participant credential is verified: it's on the site with its price, in every surface's catalog, and an order answers not_live, holds nothing, and hands over the waitlist as next. Flip it to 'live' and the same file takes orders:
→ POST /returns {"service":"transcript","taxpayer":{"name":"Avery Example","email":"avery@example.com"},"tin_last4":"4417","years":[2024,2025],"source":"ives","purpose":"mortgage underwriting","max_usd":12,"key":"tr-avery"}
{
"type": "BLOCKED",
"category": "unavailable",
"code": "not_live",
→ POST /transcript/waitlist {"email":"underwriting@lender.example"}← 200 {"object":"waitlist","service":"transcript","status":"roadmap","position":1,"simulated":true}Publish before it takes orders has every form of status.