---
title: "Notice response"
url: https://services.dev/docs/examples/tax/notice-response
captured: [tax-notice.txt]
---
# Notice response

URL: https://services.dev/docs/examples/tax/notice-response

> A letter from the IRS, answered. The AI reads it, your code matches each payment against the periods the letter shows, and sorts it. A representative claims it, the officer grants a Form 2848 that names them by CAF number for this form and these periods, and the response goes out under it. Or, for $99, the officer answers it herself.

*tax's words: return is a job, taxpayer is a subject, review is a task, preparer is a member (examples/tax/brand.ts).*

A corporation's notice is usually about money: a balance due, a payment posted to the wrong period, or a penalty. The Notice Response reads the letter, finds the facts in the taxpayer's own payments, and answers through a representative under a grant that names them.

```bash
cat services/notice-response.ts
```

```txt title="captured/tax-notice.txt · magic values: none"
import { Service } from 'services.dev'
import { classify, matchPayments } from '../lib/tax.ts'
import { preparers } from '../pools/preparers.ts'
import rules from '../rules.ts'

// A letter from the agency, answered. The AI reads it, your code matches each payment the taxpayer kept against the
// periods the letter shows them posted to, and sorts it: pay what is owed, ask for a misapplied payment to be moved, ask
// for a penalty to be abated, or reply. A representative claims it, and only then is the officer asked for a Form 2848
// that names them by CAF number, for this form and these periods, for a year, withdrawn once the reply is accepted. The
// reply date is the representative's to answer for. For $99 the officer answers it themselves, in their own name. On an
// abatement, a quarter of the penalty taken off is the fee, $99 at least. A letter asking for more time to assess gets
// the officer's signature on a Form 872, and the date it sets is kept on the taxpayer, so the year's clocks move to it.
export default Service({
  name: 'Notice Response',
  description: 'A notice, answered.',
  input: { owner: 'party: the officer who authorizes', ein: 'text: such as 12-3456789', notice: 'file: the letter', reply_by: 'date: such as 2027-12-14', tax_years: 'text[]: the periods it names, such as 2026' },
  output: { code: 'text', kind: ['agree', 'misapplied', 'penalty', 'extend', 'respond'], payments: 'json', response: 'file', receipt: 'text', abated_usd: 'money?', statute_extended_to: 'json?: by year, the date a signed Form 872 sets' },
  rules,
  subject: { kind: 'taxpayer', key: 'ein', keeps: { statute_extended_to: 'statute_extended_to' } },
  roles: { representative: { pool: preparers, pay: 90 } },
  deliver: {
    read: { by: 'ai', cite: 'source' },
    match: { by: matchPayments, cite: "each payment's receipt, and the period it was for" },
    classify: { by: classify, routes: { agree: 'pay', misapplied: 'respond', penalty: 'respond', '*': 'respond' } },
    respond: { by: 'representative', act: 'represent', needs: 'poa', stamp: true, est: '1h', drafts: { misapplied: 'a transfer request citing the receipt', penalty: 'a request to abate', extend: 'a Form 872 limited to the issues and a date' } },
    extend_statute: { ask: 'owner', to: 'sign', what: 'Form 872', after: 'respond', when: job => job.output.kind === 'extend', how: 'esign', capacity: 'officer' },
    poa: { ask: 'owner', to: 'grant', after: 'respond.claim', what: 'Form 2848', names: { claimant: 'caf' }, scope: job => ({ acts: ['represent', 'send'], matters: [{ tax: 'income', form: '1120', periods: job.input.tax_years }], for: '365d' }), how: 'esign', capacity: 'officer', withdrawn: ['accepted', 'for'] },
    send: { submit: 'https://correspondence.example.test/replies', to: 'the agency', receipt: 'receipt', needs: 'poa' },
    pay: { routed: true, submit: 'https://treasury.example.test/payments', to: 'Treasury', receipt: 'receipt' },
  },
  clocks: { reply: { due: job => job.input.reply_by, owner: 'representative', rungs: ['notice 10d', 'request time 5d', 'ack daily 3d'] } },
  price: { usd: 299, self: 99, outcome: { on: 'abated_usd', share: 0.25, min: 99, when: job => job.output.kind === 'penalty' } },
})
```

| Key | Here | What it does |
|---|---|---|
| `match` | `matchPayments`, your code | Lists each payment the brand holds a receipt for, the period it was for, and the period the letter shows it posted to |
| `classify` | `routes` by kind | Agree and pay; a misapplied payment, a penalty to abate, or anything else, to the representative |
| `respond.drafts` | by kind | The AI drafts a transfer request citing the receipt, or a request to abate, for the representative to stamp |
| `poa.after` | `'respond.claim'` | The grant is asked only once a representative claims, so it can name them |
| `poa.what`, `poa.names` | `'Form 2848'`, `{ claimant: 'caf' }` | The authorization names the representative who claimed, by CAF number |
| `poa.scope` | represent and send, income tax, Form 1120, the letter's periods, 365 days | The acts, the matter and the span it gives |
| `poa.withdrawn` | `['accepted', 'for']` | Withdrawn once the reply is accepted, or when its span runs out |
| `needs` | `'poa'`, on `respond` and `send` | The gate refuses the step `needs_grant` until the grant is live |
| `clocks.reply` | due `reply_by`, owned by the representative | Ten days out a notice, five days out the rung that asks the IRS for more time |
| `price.self`, `price.outcome` | `99`; 25% of a penalty abated, $99 at least | The officer answers it herself, or the fee rides on what is taken off |

Here it's ordered on its own.

## What the letter says, where it goes

```bash
npx services.dev check --routes services/notice-response.ts
```

```txt title="magic values: none"
  agree       the balance is right       → pay · the owner pays it, or you route it to the Treasury · CP161
  misapplied  a payment posted wrong     → respond · a transfer request, citing the receipt · CP161 on a period the brand paid
  penalty     a penalty to take off      → respond · a request to abate · 25% of what is taken off, $99 at least · CP162, CP215
  respond     anything else              → respond · the representative's reply · a letter asking for records
```

## A CP161, read and matched

The letter says Acme owes $76,289.85 for 2026. The match finds why: the extension's deposit was posted to 2027:

```txt
→ POST /returns {"service":"notice-response","owner":{"name":"Ada Lovelace","email":"ada@example.com"},"ein":"12-3456789","notice":"cp161.pdf","reply_by":"2027-12-14","tax_years":["2026","2027"],"max_usd":299,"key":"cp161"}
```

```txt title="magic values: none"
← job_4Tx2 · waiting · on claim · $299.00 held · respond · tsk_7Hq2
  read      CP161 · balance due $76,289.85 for 2026: tax $73,710.00, failure-to-pay penalty $2,579.85 · reply by 2027-12-14
  match     rcp_7HqB · US $73,710.00 · Form 7004 for 2026, paid 2027-04-12 · posted to 2027
  classify  misapplied → respond, to draft a transfer request citing rcp_7HqB
```

## The grant names who claimed

Katherine Johnson claims the review. Only then is Ada asked for the Form 2848, naming Katherine by CAF number, for Form 1120 and both periods:

```txt
→ GET /asks/ask_7Hq2?t=…
```

```txt title="magic values: none"
← 200 ask_7Hq2 · poa · to grant · Form 2848 · Ada Lovelace · open
  names     Katherine Johnson · enrolled agent · CAF 0312-45678R · PTIN P01234568
  matters   income · Form 1120 · 2026, 2027
  acts      represent, send · for 365 days · withdrawn when the reply is accepted
```

Responding needs the grant live, and Ada hasn't given it yet:

```txt
→ POST /practitioners/returns/job_4Tx2/approve {"checklist":[]}
```

```txt title="magic values: none"
← 403 needs_grant: respond needs the poa grant live, and the party hasn't given it yet.
```

She grants it, e-signed:

```txt
→ POST /asks/ask_7Hq2?t=… {}
```

```txt title="magic values: none"
← 200 done

← job_4Tx2 · waiting · on person · $299.00 held · respond · tsk_7Hq2
```

## Responded, then sent

Katherine stamps the transfer request, and it's sent under the grant:

```txt
→ POST /practitioners/returns/job_4Tx2/approve {"checklist":[]}
```

```txt title="200 · magic values: none"

← job_4Tx2 · delivered · $299.00 charged

  grants  [{"step":"poa","form":"2848","names":{"member":"mbr_8Ea1","caf":"0312-45678R"},"scope":{"acts":["represent","send"],"matters":[{"tax":"income","form":"1120","periods":["2026","2027"]}],"for":"365d"},"status":"live","until":"2028-11-13T15:00:00.000Z","withdrawn_on":"accepted"}]
```

```txt
→ GET /owner/returns/job_4Tx2   # the owner console
```

```txt title="magic values: none"
  record  read · ai · CP161 | match · code · rcp_7HqB posted to 2027 | classify · code · misapplied → respond | review · ai · pass | respond · claimed · mbr_8Ea1 | poa · asked · names mbr_8Ea1, CAF 0312-45678R | respond · representative · refused · needs_grant · respond needs the poa grant live, and the party hasn't given it yet. | poa · owner · Form 2848 · esign | respond · person · enrolled agent · * · mbr_8Ea1 · a transfer request citing rcp_7HqB · stamped | send · the agency · accepted · rcp_7Hq2
  clocks  reply · due 2027-12-14 · mbr_8Ea1 · satisfied · its request-time rung, 5 days out, never needed
  ledger  +$299.00 · AI $0.84 · representative $90.00 · you $208.16
```

## Or answered yourself

With `posture: 'self'`, the AI drafts the same response, Ada signs and sends it herself, and the grant is skipped. It costs $99. On a penalty the IRS takes off, the fee is a quarter of what is abated: [the money](https://services.dev/docs/examples/tax/money.md#paid-on-whats-abated).

[Grants and decisions](https://services.dev/docs/service/grants.md) covers `scope`, `needs` and revoking a grant. In [the year](https://services.dev/docs/examples/tax/years.md), the plan mints this Service from a November notice.

---

- Up: [The tax example](https://services.dev/docs/examples/tax.md)
- Previous: [Tax question](https://services.dev/docs/examples/tax/question.md)
- Next: [Amended return](https://services.dev/docs/examples/tax/amended-return.md)
