ワンクリックで
submit-forma-reimbursement
Use for submitting Forma reimbursement claims from receipt files.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Use for submitting Forma reimbursement claims from receipt files.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | submit-forma-reimbursement |
| description | Use for submitting Forma reimbursement claims from receipt files. |
Automate submitting one or more expense reimbursement claims to Forma. Receipt-first: read all receipts, infer all fields, confirm with user in one pass, then drive the browser — opening /claims/new once per claim in a single session.
Step 1.1 — Get receipts
If the user did not provide any receipt file paths when invoking the skill, ask:
"Path(s) to receipt file(s)? (PDF or image; separate multiple paths with spaces or newlines)"
Accept one or more paths. Process each receipt independently in steps 1.2–1.4, then present a combined confirmation block in step 1.5.
Step 1.2 — Read each receipt
For each receipt file, use the Read tool. Claude natively handles PDFs and images (PNG, JPG, HEIC).
Extract these fields per receipt:
| Field | What to look for |
|---|---|
| Vendor name | Company/service provider (e.g., "AT&T", "Xfinity", "Udemy") |
| Full billed amount | Total due or subscription charge on the receipt |
| Date | Service date or billing date — use MM/DD/YYYY format |
| Service type | What kind of service (mobile plan, internet, course, book, etc.) |
Step 1.3 — Infer reimbursement type
Map vendor/service type → one of the three Forma reimbursement types:
| Receipt type | Forma reimbursement type |
|---|---|
| Phone carrier (T-Mobile, Rogers, Verizon, AT&T, Bell, Telus) | Cell Phone/Internet Reimbursement |
| ISP / internet service (Xfinity, Comcast, Spectrum, Rogers Internet) | Cell Phone/Internet Reimbursement |
| Online courses, e-learning (Udemy, Coursera, Pluralsight, LinkedIn Learning) | Personal Professional Development Budget |
| Vocabulary, writing, language-learning, or communication-improvement subscriptions | Personal Professional Development Budget |
| Spotify, Audible, or YouTube Premium subscriptions with a truthful note explaining connection to professional development | Personal Professional Development Budget |
| Developer tools, software subscriptions (GitHub, JetBrains, 1Password work, etc.) | Personal Professional Development Budget |
| Technical books, Amazon tech purchases | Personal Professional Development Budget |
| Conferences, workshops, training events | Personal Professional Development Budget |
| Wellness, gym, spa, dining, entertainment | Treat Yourself |
| Annual personal enrichment subscription | Treat Yourself |
If the vendor doesn't map cleanly to any type, use AskUserQuestion to present all three options and ask the user to pick.
Step 1.4 — Determine claim amount
The claim amount is what will be submitted to Forma — it may differ from the full billed amount.
For Cell Phone/Internet Reimbursement:
The monthly benefit is $60. Apply this logic:
When adjusting an amount, note the adjustment explicitly in the confirmation block (step 1.5) so the user can see and approve it.
For all other reimbursement types:
Use the full billed amount as the claim amount (no cap).
Step 1.5 — Generate description
Format: "<Vendor> <service description> – <Month YYYY>"
Use the billing date's month and year.
Examples:
"AT&T monthly cell phone bill – March 2026""Xfinity internet service – March 2026""Udemy online course – February 2026"Step 1.6 — Present confirmation block
After processing all receipts, present a combined summary and wait for the user's explicit confirmation:
Receipts detected:
#1 Vendor: <vendor>
Date: <date>
Billed: $<full billed amount>
Claim: $<claim amount> [← adjusted: $60 cap / prior overage of $X applied]
Type: <reimbursement type>
Desc: "<description>"
#2 Vendor: <vendor>
Date: <date>
Billed: $<full billed amount>
Claim: $<claim amount>
Type: <reimbursement type>
Desc: "<description>"
Confirm all? (or tell me what to change — e.g. "change #2 amount to $55")
If the user requests corrections, apply them and proceed with corrected values. Do not re-read the receipts.
Choose the browser backend based on what's available:
agent-browser-kg skillforma-submit.ts in this skill directory, or run equivalent WebDriver codeNote on form field availability: Forma's claims form shows different fields depending on the selected reimbursement type. For Cell Phone/Internet Reimbursement, the pre-scan form shows only: Category dropdown + Description field + Upload button. Amount and Date fields appear post-scan (after the receipt is processed by Forma). Other reimbursement types may show more fields pre-scan. Adapt to what the snapshot reveals — do not assume a static layout.
CRITICAL: Do not override auto-extracted fields. After Forma scans the receipt, it auto-populates Amount, Merchant, and Date. Manually overwriting these fields breaks Forma's React validation and blocks submission. Only verify the auto-extracted values match the confirmed values; do not touch the fields unless the extraction is clearly wrong.
Step 2.1 — Check for saved auth state
Run:
ls ~/.local/state/agent-browser/states/joinforma.json
If the file does NOT exist, print the following first-run setup message and stop — do NOT proceed to browser automation:
First-run setup required. To save your Forma auth state:
1. Open Forma in headed Brave:
agent-browser \
--executable-path "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" \
--session-name joinforma \
--headed \
open https://client.joinforma.com/
2. Log in with your Work account credentials in the browser window.
3. Once logged in, save the auth state:
agent-browser --session-name joinforma state save ~/.local/state/agent-browser/states/joinforma.json
4. Re-invoke /submit-forma-reimbursement
Step 2.2 — Launch Brave headed and load auth state
Always use headed mode (skip lightpanda and headless Brave — Forma is a React SPA and its OCR backend appears to fingerprint headless requests, causing the receipt scan to silently time out):
agent-browser \
--executable-path "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" \
--session-name joinforma \
--headed \
open https://client.joinforma.com/claims/new
Load saved auth state explicitly (do NOT rely on auto-restore — known bug):
agent-browser --session-name joinforma state load ~/.local/state/agent-browser/states/joinforma.json
Reload the page so the injected cookies take effect:
agent-browser --session-name joinforma open https://client.joinforma.com/claims/new
Take a snapshot to verify the form is loaded:
agent-browser --session-name joinforma snapshot -i
Inspect the snapshot. If it shows a login/sign-in page instead of the claims form, the saved state has expired. Print this message and stop:
Auth state expired. Please refresh it:
1. agent-browser \
--executable-path "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" \
--session-name joinforma \
--headed \
open https://client.joinforma.com/
2. Log in again.
3. agent-browser --session-name joinforma state save \
~/.local/state/agent-browser/states/joinforma.json
4. Re-invoke /submit-forma-reimbursement
Prerequisites:
firefox -no-remote -profile ~/.firefox-automation --marionetteselenium-webdriver npm package available~/.cache/selenium/geckodriver/mac-arm64/0.36.0/geckodriver)Auth setup (one-time):
~/.firefox-automation across restartsRunning the TypeScript script:
cd ~/notes/obsd/.agents/skills/submit-forma-reimbursement
npx ts-node forma-submit.ts \
"/path/to/receipt.pdf" \
"Personal Professional Development Budget" \
"Books" \
"Audible communication audiobook - May 2026"
The script (forma-submit.ts) handles:
input[type=file] via JS)Manual WebDriver commands (if not using the script):
import { Builder, By, until } from "selenium-webdriver";
import firefox from "selenium-webdriver/firefox";
const service = new firefox.ServiceBuilder("/path/to/geckodriver");
service.addArguments("--connect-existing", "--marionette-port=2828");
const driver = await new Builder()
.forBrowser("firefox")
.setFirefoxService(service)
.build();
await driver.get("https://client.joinforma.com/claims/new");
// Select dropdown by clicking input[aria-autocomplete="list"],
// then clicking the matching li.MuiAutocomplete-option
// Upload file: find input[type="file"], unhide via JS, then sendKeys(path)
await driver.executeScript(`
arguments[0].style.display = "block";
arguments[0].style.visibility = "visible";
arguments[0].style.opacity = "1";
`, fileInput);
await fileInput.sendKeys("/path/to/receipt.pdf");
// Click "Upload receipt" button, wait 10s for OCR
// Click Submit
Step 3.1 — Present final summary
Before clicking submit, pause and present this summary for the current claim:
Ready to submit claim #N of M:
Reimbursement type: <type>
Category: <subcategory>
Description: "<description>"
Amount: $<claim amount>
Date: <date>
Receipt: <filename> ✓ uploaded
Submit claim? [yes / no / cancel]
Wait for explicit "yes" confirmation. On "no" or "cancel", close the browser session and stop without submitting. Remaining receipts are abandoned.
Step 3.2 — Submit
On confirmation, scroll the Submit button into the visible viewport before clicking — Forma's React click handler requires the button to be on-screen:
Brave backend:
agent-browser --session-name joinforma scroll down 300
agent-browser --session-name joinforma click @eN # N = Submit button ref
Firefox/WebDriver backend:
const btn = await driver.findElement(By.xpath("//button[contains(., 'Submit')]"));
await driver.executeScript('arguments[0].scrollIntoView({block: "center"});', btn);
await driver.sleep(1000);
await btn.click();
Take a screenshot (not snapshot) to verify the outcome — the success toast
is transient and disappears before snapshot -i can capture it:
Brave backend:
sleep 3 && agent-browser --session-name joinforma screenshot
Firefox/WebDriver backend:
await driver.sleep(3000);
const screenshot = await driver.takeScreenshot();
Look for a success indicator: a "Claim submitted successfully." toast, a
"Great, your claim was submitted!" heading, or a redirect to /claims.
Report the outcome:
On failure, stop. Do not attempt remaining receipts.
Step 3.3 — Continue or close
If there are more receipts, loop back to Step 2.3a (or re-run the TypeScript script with the next receipt).
Once all receipts are submitted, close the browser session:
Brave backend:
agent-browser --session-name joinforma close
Firefox/WebDriver backend:
await driver.quit();
Report a final summary:
All claims submitted:
#1 <vendor> – <Month YYYY> → $<amount> ✓
#2 <vendor> – <Month YYYY> → $<amount> ✓
...
| Scenario | Action |
|---|---|
joinforma.json not found (Brave) | Print first-run setup instructions (Step 2.1). Stop. Do not open browser. |
| Firefox auth expired (login page shown) | User must re-log in via Firefox automation profile. |
| Snapshot shows login page after state load | Print state-expired instructions (Step 2.2). Close session. Stop. |
| Subcategory dropdown empty or unrecognised layout | Snapshot and describe what you see. Ask user to identify the right element before continuing. |
| Upload fails or processing modal does not clear | Snapshot and report. Ask user whether to retry manually in a browser window; continue once they confirm the receipt is attached. |
| Post-scan Amount or Date field not found | Snapshot and report what is visible. Do not proceed to submit without confirming both fields are set. |
| Amount not extractable from receipt | Ask user to provide the amount explicitly before Phase 2. |
| Date not extractable from receipt | Ask user to provide the date explicitly before Phase 2. |
| Vendor doesn't map to a reimbursement type | Use AskUserQuestion to present all three types. Let user pick. |
| Submission fails with page error | Snapshot and report the error message verbatim. Do NOT retry automatically. Stop. Abandon remaining receipts. |
| Auto-extracted values look wrong | Only override if clearly wrong. Overwriting breaks Forma validation. |