CoreTax Console
Chrome extension for automated tax invoice extraction from DJP CoreTax — XHR intercept, multi-sheet XLSX, and 7-sheet Inter Mode.
CoreTax Console — Overview
Chrome Extension
ACTIVECoreTax Console is a Chrome Extension that automates tax document operations from the
Indonesian DJP CoreTax system (coretax.pajak.go.id).
It intercepts API responses, extracts tabular data, downloads PDFs, and generates multi-sheet
XLSX exports — all without requiring manual page navigation or clicking.
Core Capabilities
| Feature | Description | Output |
|---|---|---|
| Extractor | Extract tabular data from CoreTax API responses | XLSX / CSV |
| Console (PDF Download) | Download PDF invoices via XHR replay or DOM click | PDF files |
| Inter Mode | Extract complete invoice details via /view endpoint replay | 7-sheet XLSX |
| Faktur Masukan Pipeline | 3-step pipeline: Scan → Extract → Download for input invoices | XLSX |
| Auto Renamer | Smart filename renaming via data matching (not OCR) | Renamed PDFs |
| PPh Calculator | Built-in PPh calculator injected into CoreTax pages | In-page widget |
Architecture — 4 Execution Contexts
The extension operates across four Chrome execution contexts, each with distinct capabilities.
Communication flows through Chrome Extension APIs between contexts, and via window.postMessage between ISOLATED and MAIN worlds.
| Context | World | Responsibilities |
|---|---|---|
| Background Service Worker | Extension | Message routing, job lifecycle, download tracking, state persistence, auth management |
| Sidepanel UI | chrome://side-panel | User interface — Welcome, Console, Extractor, Faktur Masukan, Subscription, Settings |
| Content Script | ISOLATED | DOM manipulation, XHR bridge relay, FM handlers, capture-click logic |
| MAIN World Scripts | MAIN | XHR/fetch interception, pagination guard, confirm/alert bypass |
3-Layer XHR Architecture
All XHR-based modes (Spam, Bot, Capture) use a 3-layer relay to bridge the gap between the Sidepanel and CoreTax's network requests:
┌─────────────────┐ chrome.runtime.sendMessage ┌──────────────────┐ window.postMessage ┌──────────────────┐ │ Sidepanel UI │ ──────────────────────────────────► │ XHR Bridge │ ─────────────────────────────► │ XHR Scraper │ │ (chrome:// │ (xhr:* prefix messages) │ (ISOLATED world)│ (FROM_CONTENT/FROM_PAGE) │ (MAIN world) │ │ side-panel) │ ◄────────────────────────────────── │ │ ◄─────────────────────────── │ │ └─────────────────┘ response via sendResponse └──────────────────┘ response via callback └──────────────────┘ Layer 1 — Sidepanel: User controls, export triggers, mode selection Layer 2 — XHR Bridge: Relay between Sidepanel ↔ MAIN world (ISOLATED) Layer 3 — XHR Scraper: Intercepts XHR/fetch, replays requests, captures responses
Supported Data Sources
| Source | List Endpoint | Detail Endpoint | Download | Inter Mode |
|---|---|---|---|---|
| OUTPUT_TAX | /outputinvoice/list | /outputinvoice/view | ✅ | ✅ |
| INPUT_TAX | /inputinvoice/list | /inputinvoice/view | ✅ | ✅ |
| OUTPUT_RETURN | /outputreturn/list | /outputreturn/view | ✅ | ✅ |
| INPUT_RETURN | /inputreturn/list | /inputreturn/view | ✅ | ✅ |
| SPECIAL_OUTPUT_TAX | /specialdocumentoutputinvoice/list | /specialdocumentoutputinvoice/view | ✅ | ✅ |
| SPECIAL_INPUT_TAX | /specialdocumentinputinvoice/list | /specialdocumentinputinvoice/view | ✅ | ✅ |
| DOC_MANAGEMENT | /listTaxpayerDocuments | — | ✅ | ❌ |
| WITHHOLDING_SLIPS | /GetMyWithholdingSlip/list | — | ✅ | ❌ |
| SPT_A2 | /returnsheetsportal | — | ❌ | ❌ |
| SPT_B2 | /returnsheetsportal | — | ❌ | ❌ |
| PPH_21/26 | /returnsheetsportal | — | ❌ | ❌ |
XHR Extraction Modes
Three Modes for Data Extraction
The Extractor provides three modes for retrieving tabular data from CoreTax API responses. All three operate via XHR interception — no manual page navigation or DOM clicking required. The same three modes also apply to PDF downloads via XHR replay (Inter mode).
Spam Mode
FASTESTAuto-paginate through all data by replaying the captured XHR request with incrementing offset parameters and a large row count. The fastest way to extract bulk data — no DOM interaction at all.
| Parameter | Value | Note |
|---|---|---|
| Method | XHR replay with modified First/Rows params | Replays captured request |
| rows per request | 500 (E-Invoice), 100 (Doc Mgmt), 1000 (SPT/PPh) | Varies by source payload size |
| Delay between requests | 100ms (E-Invoice), 150ms (others) | Lightweight API calls |
| Error tolerance | 3 consecutive errors | Stops after 3 consecutive failures |
| Warm cache | Used for first page only | Reduces 1 redundant request |
Bot Mode
CONTROLLEDSimulates sequential page navigation by clicking the "Next Page" button in the DOM, then capturing the XHR response that Angular generates. More controlled than Spam — each page is processed before moving to the next.
| Parameter | Value | Note |
|---|---|---|
| Method | Click Next Page → capture XHR response | Simulates user pagination |
| Page detection | Row count < pageSize or duplicate data | Auto-stops at last page |
| Paginator wait | 100ms polling, 2000ms timeout | Waits for Angular to render |
| Error tolerance | 3 consecutive failures | Same as Spam Mode |
| Inter Mode integration | Optional — runs detail extraction per page | Extract then advance |
Capture Mode
GRANULARExtract only the items you select. Check the rows you want in the CoreTax table, then run Capture Mode to extract just those items. Ideal for targeted extraction without processing the entire dataset.
| Parameter | Value | Note |
|---|---|---|
| Matching strategy | Row index (primary) + highlighted rows (fallback) | Maps checked items to XHR data |
| Scope | Single page only | Checkbox state does not persist across pages |
| Warm cache | Critical — avoids re-clicking Search | Uses 5-minute cached response |
| Checkbox preservation | Snapshot before, restore after | UI unchanged after extraction |
Mode × Source Compatibility
| Source | Spam | Bot | Capture | Download | Inter | Click Mode |
|---|---|---|---|---|---|---|
| OUTPUT_TAX | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| INPUT_TAX | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| OUTPUT_RETURN | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| INPUT_RETURN | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| SPECIAL_OUTPUT_TAX | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| SPECIAL_INPUT_TAX | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| SPT_A2 | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| SPT_B2 | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| PPH_21/26 | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| WITHHOLDING_SLIPS | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| DOC_MANAGEMENT | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
DOM Click Mode
When XHR Replay Isn't Possible
FALLBACKSome CoreTax download endpoints use CSRF tokens that change per request, making XHR replay impossible. DOM Click Mode handles these cases by physically clicking download buttons in the page — the same way a human would, but automated.
How It Works
| Sub-Mode | Strategy | Use Case |
|---|---|---|
| Bot (Click) | Click all download buttons per page, track tickets, navigate next | Bulk PDF download for all items |
| Capture+Click | Click download buttons only for checked/selected items | Targeted PDF download for specific items |
Ticket-Based Download Tracking
Every button click creates a "ticket" tracked by the chrome.downloads API.
The extension monitors onCreated and
onChanged events to match
each download to its ticket, ensuring no files are lost or double-counted.
// Download tracking flow 1. Click download button in DOM 2. Chrome creates download item → onCreated event 3. Match download URL/filename to pending ticket 4. Monitor onChanged → state: 'complete' | 'interrupted' 5. Mark ticket as done → proceed to next item // Batch limit: 10 items per page (CLICK_BATCH_SIZE) // Burst delay: progressive increase between batches
Self-Resume After Page Reload
Downloads can trigger page reloads in CoreTax. Capture+Click Mode persists its state to
localStorage before each click.
If the page reloads, the extension reads the saved state and resumes from the last unfinished item.
State older than 5 minutes is considered stale and discarded.
// Self-resume mechanism 1. Before each click → save pending state to localStorage 2. On page load → check for saved state 3. If state exists and < 5 minutes old → resume 4. If state is stale → start fresh 5. After all items done → clear saved state
Downloadability Rules (E-Invoice)
Not all invoices have a downloadable PDF. The extension automatically skips items that would result in errors, saving time and bandwidth:
| Condition | Action | Reason |
|---|---|---|
| Status = CREATED | Skip | PDF not yet generated, no TaxInvoiceNumber, not e-Signed |
| CREDITED without DocFormAggId | Skip | Invoice replaced by correction — PDF is obsolete |
| No DocFormAggId at all | Skip | Cannot construct download URL without identifier |
Inter Mode — Detail Extraction
Complete Invoice Data in 7 Sheets
V2Inter Mode combines list data with detail data from XHR replay to the
/view endpoint,
producing XLSX files with 7 sheets of complete invoice data —
without navigating or clicking Edit. Available for all 6 e-invoice sources.
7-Sheet Output
| Sheet | Data Source | Records | Key Fields |
|---|---|---|---|
| 1. Ringkasan | /list API | 1 per invoice | RecordId, NPWP, DPP, PPN, Status |
| 2. Header Detail | /view → Payload.* | 1 per invoice | AggregateVersion, InvoiceType, SellerTIN |
| 3. Doc Transaksi | /view → FormData.TransactionDocumentData | 1 per invoice | TransactionCode, SellerAddress |
| 4. Data Pembeli | /view → FormData.BuyerInformationData | 1 per invoice | BuyerTIN, IDDocument, Email |
| 5. Detail Barang | /view → FormData.TransactionDetailsData.Rows | 2-10x invoices | Name, Qty, UnitPrice, VATRate |
| 6. Ringkasan/Faktur | /view → FormData.FooterRow | 1 per invoice | TotalPrice, TaxBaseTotal, VATTotal |
| 7. Metadata | /view → FormData top-level | 1 per invoice | IsDraft, IsMigrated |
API Request Structure
POST /einvoiceportal/api/outputinvoice/view
{
"RecordIdentifier": "<from list .RecordId>",
"EinvoiceVATStatus": "VAT_VAT",
"TaxpayerAggregateIdentifier": "<from list .Seller* or .Buyer* AggregateIdentifier>"
}
// Response: Payload.FormData is a JSON STRING — must parse twice
// Parse → extract TransactionDocumentData, BuyerInformationData,
// TransactionDetailsData.Rows[], FooterRow, metadata
// Headers auto-captured:
// content-type: application/json
// languageid: id-ID
// x-dgt-code: <auto-captured CSRF token>Supported Sources
| Source | Detail Endpoint | AggId Source | Non-Downloadable Status |
|---|---|---|---|
| OUTPUT_TAX | /outputinvoice/view | SellerTaxpayerAggregateIdentifier | CREATED |
| INPUT_TAX | /inputinvoice/view | BuyerTaxpayerAggregateIdentifier | CREDITED (no DocForm) |
| OUTPUT_RETURN | /outputreturn/view | SellerTaxpayerAggregateIdentifier | CREATED |
| INPUT_RETURN | /inputreturn/view | BuyerTaxpayerAggregateIdentifier | CREDITED (no DocForm) |
| SPECIAL_OUTPUT_TAX | /specialdocumentoutputinvoice/view | SellerTaxpayerAggregateIdentifier | CREATED |
| SPECIAL_INPUT_TAX | /specialdocumentinputinvoice/view | BuyerTaxpayerAggregateIdentifier | CREDITED (no DocForm) |
Execution Parameters
| Parameter | Value | Note |
|---|---|---|
| Delay between requests | 500ms | Heavier than list requests (100ms) |
| Max concurrent | 1 (serial) | Sequential execution only |
| Error retry per item | 2x | Retry twice before skip |
| Max consecutive errors | 5 | Abort entire job after 5 consecutive |
| Request timeout | 30 seconds | Same as list requests |
| Session expiry (401/403) | Immediate abort | User must re-login |
Sheet Toggle Settings
All 7 sheets can be individually enabled or disabled. Toggle 1 (Ringkasan) controls whether the sheet
appears in the output — but list data is always collected because RecordId and TaxpayerAggregateIdentifier
are needed for /view API calls.
If only Toggle 1 is active and Toggles 2–7 are off, the extension produces a list-only XLSX (no Inter Mode).
// Settings keys (all default: true) interSheet1Ringkasan: true // Sheet 1 — can be disabled interSheet2Header: true // Sheet 2 interSheet3DocData: true // Sheet 3 interSheet4Buyer: true // Sheet 4 interSheet5Items: true // Sheet 5 — MOST IMPORTANT interSheet6Footer: true // Sheet 6 interSheet7Meta: true // Sheet 7
Comparison: List Only vs Click Mode vs Inter Mode
| Aspect | List Only | Click Mode (DOM) | Inter Mode (XHR) |
|---|---|---|---|
| Transaction item data | ❌ | ✅ | ✅ |
| Full buyer info | ⚠️ Limited | ✅ | ✅ |
| Transaction document data | ❌ | ✅ | ✅ |
| Per-invoice summary | ⚠️ Totals only | ✅ | ✅ |
| Metadata (draft, migrated) | ❌ | ⚠️ Limited | ✅ |
| Speed (151 invoices) | ~30 seconds | ~30+ minutes | ~2-3 minutes |
| Reliability | ✅ Stable API | ⚠️ DOM fragile | ✅ Stable API |
| Requires navigation | ❌ | ✅ | ❌ |
| XLSX output | 1 sheet | 1 sheet (flat) | 7 sheets (relational) |
Faktur Masukan Pipeline
3-Step Pipeline for Input Invoices
PIPELINEThe Faktur Masukan (Input Invoice) pipeline is a specialized 3-step process for extracting data from invoices that don't have download buttons. It opens edit pages, extracts 21 columns of transaction data, and exports to XLSX with formula injection protection.
Pipeline Steps
Step 1: SCAN — Find invoices without download buttons ───────────────────────────────────────────────── • Scan every row in the FM table • Rows without #DownloadButton → marked as "pending" • Bot mode: scan up to 500 pages • Capture mode: scan current page only • Output: pendingInvoices[] Step 2: EXTRACT — Open edit pages and collect data ───────────────────────────────────────────────── • For each pending invoice: 1. Filter by invoice number in the search field 2. Click the "Edit" button → navigate to edit page 3. Wait for page to load (DOM polling, not sleep) 4. Extract 14 columns of transaction rows 5. Navigate back (history.back()) • Retry: 3 inner retries + 2 outer retries per invoice • Output: excelRows[] Step 3: DOWNLOAD — Build and save the XLSX file ───────────────────────────────────────────────── • Build workbook with SheetJS (21 columns) • Sanitize cells (formula injection + leading zero protection) • Encode as base64 data URL (MV3 constraint) • Trigger download via chrome.downloads API
XLSX Column Layout (21 Columns)
| Group | Columns | Source | Examples |
|---|---|---|---|
| Metadata (7 cols) | Tanggal, Masa Pajak, Nomor Faktur, Reference, Nama Penjual, Status, Jenis | List page rows | 01/06/2026, Juni 2026, 001.000.26.00000001 |
| Detail (14 cols) | Tipe, Nama, Kode, Kuantitas, Satuan, Harga Satuan, Total Harga, Potongan, Tarif PPN, DPP, PPN, DPP Nilai Lain, PPnBM, Tarif PPnBM | Edit page rows | Barang, Jasa Konsultasi, 10, Unit, 1.234.567,89 |
Bot vs Capture Mode
| Aspect | Bot Mode | Capture Mode |
|---|---|---|
| Scan scope | Up to 500 pages | Current page only |
| User steps | 3 clicks (Find → Extract → Download) | 1 click (Capture Excel — auto-extract & download) |
| Filter application | Re-applies year/month filter | Skips filter (already on right page) |
| Speed | Slower (multi-page navigation) | Fast (single page) |
| Use case | Bulk extraction of all pending invoices | Quick extraction of visible items |
Data Protection
Auto Renamer
Smart PDF Renaming via Data Matching
NOT OCRThe Auto Renamer renames downloaded PDF files by matching them against invoice data already captured by the extension — not by reading the PDF contents. This means renaming is instant, accurate, and works even with scanned or image-based PDFs.
How Data Matching Works
1. Extension monitors downloads from coretax.pajak.go.id
2. When a PDF download completes:
a. Match the downloaded file to an invoice in the extracted data
b. Use document type, invoice number, and other identifiers
c. Rename the file based on matched data
3. Naming pattern varies by document type:
• E-Invoice: {InvoiceNumber}.pdf
• Bukti Potong: {BupotNumber}.pdf
• Doc Management: {DocType}_{Identifier}.pdf
4. Fallback: If no match found, keep original filenameWhy Not OCR?
| Aspect | Data Matching (Our Approach) | OCR-Based Renaming |
|---|---|---|
| Speed | Instant — matches against cached data | Slow — requires PDF parsing + text extraction |
| Accuracy | 100% for matched items — uses API data | Variable — depends on PDF quality, scan resolution |
| Coverage | Works with all PDFs (including scanned) | Fails on image-only or low-quality scans |
| Resource usage | Minimal — no PDF processing needed | High — requires PDF library + OCR engine |
| Dependencies | None — uses already-captured data | Requires pdf.js or server-side OCR |
Authentication
Dual Authentication System
The extension uses two fully independent authentication systems. They never exchange tokens, never share credentials, and serve completely different purposes. You can use CoreTax features without Google login, and check your subscription without opening CoreTax.
| System | Purpose | How It Works |
|---|---|---|
| CoreTax Session | Access DJP APIs for data extraction and PDF downloads | Reuses browser session cookies + CSRF tokens automatically |
| Google Account (Fought Auth) | Subscription management, company access, ad-free experience | Google OAuth via Chrome Identity API → JWT session token |
CoreTax Session (Automatic)
No separate login required. The extension operates within your authenticated CoreTax browser session. All API requests reuse the credentials already present in the browser. Just make sure you're logged into CoreTax in the same tab.
| Credential | Source | Purpose |
|---|---|---|
| Session cookies | Browser cookie store | CoreTax API authentication |
| CSRF token (x-dgt-code) | Auto-captured from XHR headers | Request validation for API calls |
| Access token | Intercepted from fetch requests | JWT for CoreTax API authorization |
| Taxpayer ID | localStorage.userinfo | NPWP / taxpayer identification |
Google Account (Fought Auth)
OAUTHSign in with Google to manage subscriptions and company access. The login flow uses Chrome's built-in Identity API — your Google credentials are never directly handled by the extension.
Security Measures
| Measure | Implementation |
|---|---|
| Auth token storage | chrome.storage.session — survives SW restart, clears on browser close |
| Rate limiting | Sliding window — 10 req/min for auth, 30 req/min for API |
| In-flight deduplication | Multiple concurrent /auth/me requests share one Promise |
| Session validation | Periodic server-side check with 2-minute grace period for new tokens |
| Auto signout | 401 from server triggers automatic cleanup of all auth data |
Access Tiers
Three-Tier Access System
| Tier | How Obtained | Ads | Extractor Access | Donation UI |
|---|---|---|---|---|
| Free | Default — no sign-up required | Probability-based interstitial ads | ✅ Available | Visible |
| Subscriber | QRIS payment (Rp 100,000 / 30 days) | Ad-free experience | ✅ Available | Visible |
| Company | B2B license via your organization | Ad-free experience | ✅ Available | Hidden |
All tiers have access to the same core features — Console, Inter Mode, Faktur Masukan, Auto Renamer. The only differences are ad presence and company-specific UI adjustments.
Access Hierarchy
hasFullAccess() = isCompanyUser() || isSubscriptionActive()
Priority order:
1. Company User → ALWAYS full access (highest priority)
No ads, no subscription needed, no donation UI
2. Subscriber → Full access until subscription expires
No ads, all features
3. Free User → All features with interstitial ads
Probability-based ad display
Company access takes priority over everything:
• Even if subscription is expired → full access
• Even if subscription is active → company badge shown
• Admin can revoke company access at any timeSubscription via QRIS
Individual subscriptions are processed via QRIS (Quick Response Code Indonesian Standard), the standard QR payment method in Indonesia. Payment flow:
1. Click "Subscribe" in the sidepanel 2. Extension creates payment request → server generates QR code 3. Scan QR code with any Indonesian payment app (GoPay, OVO, Dana, etc.) 4. Payment confirmed via webhook (RSA-SHA256 verified) 5. Extension polls status → subscription activated automatically Price: Rp 100,000 (≈ US $6) Period: 30 days Identity: email_id (SHA-256 of normalized email)
Company Access
Company access is granted by your organization's administrator through a B2B license. Company users always get full access regardless of subscription status, see no ads, and the donation UI is automatically hidden. Company status is synced on extension install and browser startup — if an admin revokes access, it's reflected on next launch.