Skip to content

feat: Mercury webhook real-time classification + ChittySchema integration#90

Merged
chitcommit merged 1 commit intomainfrom
feat/mercury-webhook-classification
Apr 10, 2026
Merged

feat: Mercury webhook real-time classification + ChittySchema integration#90
chitcommit merged 1 commit intomainfrom
feat/mercury-webhook-classification

Conversation

@chitcommit
Copy link
Copy Markdown
Contributor

@chitcommit chitcommit commented Apr 10, 2026

Summary

Completes Phase 2 of the COA trust-path initiative by adding real-time classification at Mercury webhook ingest and modernizing the ChittySchema client for Cloudflare Workers.

Mercury webhook flow

The Mercury webhook (POST /api/webhooks/mercury) previously only deduped and logged. It now persists transactions to the DB with auto-classified suggestedCoaCode populated at ingest time (L0 → L1 keyword suggestion).

Step Behavior
1. Service auth Bearer token == CHITTY_AUTH_SERVICE_TOKEN, else 401
2. Envelope validation Zod: {id, type, data.transaction} with UUID tenant/account
3. KV idempotency webhook:mercury:${eventId} with 7-day TTL
4. Advisory schema validation validateRow() against FinancialTransactionsInsertSchema — never blocks
5. DB dedup externalId = mercury:${mercuryTransactionId} unique check
6. Classify + persist findAccountCode()suggestedCoaCode + confidence, then createTransaction()
7. Audit ledgerLog entry with schemaValid + schemaAdvisory flags

Envelope-only events (e.g. account.created) still get 202 without persistence.

ChittySchema integration

server/lib/chittyschema.ts is a fresh Workers-native client. It replaces two stale modules that were never imported but carried the wrong API paths:

  • server/lib/chittyschema-validation.ts — used /api/v1/validate (404), Express middleware, process.env
  • ChittySchemaClient in chittyos-client.ts — same issues

Verified live against https://schema.chitty.cc:

  • GET /api/health{status:"ok",service:"chittyschema",version:"1.0.0"}
  • POST /api/validate with {table,data}{valid,errors,availableTables?}
  • GET /api/tables → 35 registered tables across chittyos-core, chittyledger, chittycanon

Fall-open philosophy

Validation is advisory. External schema services must never block financial writes. The client returns {ok: true, advisory: true} for:

  • 5xx responses
  • Network errors / timeouts
  • Malformed response bodies
  • Unregistered tables (with availableTables surfaced for operators)

Only a clean {valid: false, errors: [...]} response counts as a validation failure, and even then the Mercury handler logs a warning and proceeds.

Registry state

chart_of_accounts and classification_audit are not yet registered in ChittySchema — the Mercury webhook uses FinancialTransactionsInsertSchema which is already in the chittyledger database. A follow-up PR can register the COA tables with the schema team once they add a chittyfinance database namespace.

Tests (25 new, 246 total, 0 TypeScript errors)

  • server/__tests__/chittyschema.test.ts (16): validation result shapes, fall-open on 5xx/network/timeout/malformed body, unregistered-table advisory, default base URL, listTables, checkHealth
  • server/__tests__/webhooks-mercury.test.ts (10): auth rejection, invalid envelope, successful persistence with auto-classification, suspense fallback (9010 @ 0.1), KV dedup, externalId dedup, non-blocking schema failure, unreachable schema, envelope-only ack

Test plan

  • TypeScript check passes (0 errors)
  • 246 tests pass
  • ChittySchema endpoints verified live against production
  • Manual: POST a Mercury webhook payload → verify transaction appears in /classification queue with suggestedCoaCode populated
  • Manual: POST duplicate event id → verify 202 duplicate response
  • Manual: temporarily set CHITTYSCHEMA_URL to an unreachable host → verify webhook still persists with schemaAdvisory: true

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Mercury webhooks now automatically ingest transactions with intelligent Chart of Accounts (COA) categorization and confidence scoring.
    • Integrated advisory schema validation that logs issues without blocking transaction processing.
  • Tests

    • Added test coverage for ChittySchema validation service and Mercury webhook handler.
  • Chores

    • Updated environment configuration to support custom ChittySchema service endpoints.

…egration

Mercury webhook (/api/webhooks/mercury) now persists transactions to the
DB with auto-classified suggestedCoaCode populated at ingest time. This
completes the L0 ingest path of the trust-path model for the real-time
Mercury flow (previously the webhook only deduped and logged, never
persisted).

Flow:
  1. Service-auth check (Bearer token)
  2. Zod envelope validation
  3. KV idempotency (7-day TTL)
  4. Advisory ChittySchema validation (never blocks)
  5. DB dedup via externalId (retries-safe)
  6. createTransaction with suggestedCoaCode + classificationConfidence
  7. ledgerLog audit entry

Auto-classification uses findAccountCode() keyword matcher (same path as
TurboTenant import). Suspense (9010) gets 0.100 confidence; matched codes
get 0.700.

ChittySchema integration (server/lib/chittyschema.ts):
- New Workers-native client — no process.env at module load
- Speaks the real ChittySchema API (/api/health, /api/validate, /api/tables)
- Fall-open on 5xx / network error / timeout / malformed body — never
  blocks financial writes
- Advisory pass when table is not registered (surfaces availableTables
  so operators can see what's in the registry)
- Normalizes error shapes (string[] and object[] both supported)
- Three functions: validateRow, listTables, checkHealth

Cleanup:
- Deleted server/lib/chittyschema-validation.ts — stale, used wrong API
  paths (/api/v1/validate), had Express middleware (dead code), relied on
  process.env which doesn't work in Workers. Never imported anywhere.
- Removed ChittySchemaClient class and getChittySchema factory from
  chittyos-client.ts — same issues, never imported.

Added CHITTYSCHEMA_URL? to the env type for optional override.

Tests (25 new, 246 total):
- server/__tests__/chittyschema.test.ts (16): valid/invalid results,
  fall-open on 5xx/network/timeout/malformed-body, unregistered-table
  advisory pass, default base URL, listTables, checkHealth
- server/__tests__/webhooks-mercury.test.ts (10): auth rejection,
  invalid envelope, successful persistence with auto-classification,
  suspense fallback, KV dedup, externalId dedup, non-blocking schema
  validation failure, unreachable schema, envelope-only ack

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@chitcommit chitcommit enabled auto-merge (squash) April 10, 2026 23:30
Copilot AI review requested due to automatic review settings April 10, 2026 23:30
@github-actions
Copy link
Copy Markdown
Contributor

@coderabbitai review

Please evaluate:

  • Security implications
  • Credential exposure risk
  • Dependency supply chain concerns
  • Breaking API changes

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR replaces the deprecated ChittySchema integration with a new worker-compatible module. It introduces chittyschema.ts with functions that accept env instead of reading process.env at module load, removes the old ChittySchemaClient and chittyschema-validation.ts, integrates advisory schema validation into the Mercury webhook handler, and adds comprehensive test coverage for the new functionality.

Changes

Cohort / File(s) Summary
New ChittySchema Module
server/lib/chittyschema.ts
Added worker-compatible module exporting validateRow, listTables, and checkHealth functions. Implements advisory validation that falls open on network errors, timeouts, and 5xx responses; supports table registration checks and error normalization.
Environment Configuration
server/env.ts
Extended Env interface with optional CHITTYSCHEMA_URL environment variable for schema service endpoint override.
Mercury Webhook Integration
server/routes/webhooks.ts
Enhanced handler to parse envelope via Zod, compute COA code and classification confidence, perform non-blocking advisory validation, implement DB-level dedup via externalId, persist transaction metadata, and emit audit logs via ledgerLog.
Old Schema Validation Removal
server/lib/chittyos-client.ts
Removed ChittySchemaClient class, factory method getChittySchema(), and convenience function chittySchema() with inline note redirecting to new module.
Deprecated Validation Module Removal
server/lib/chittyschema-validation.ts
Deleted entire module including validation middleware, batch validators, entity-type discovery, and health checks (254 lines).
ChittySchema Module Tests
server/__tests__/chittyschema.test.ts
Added Vitest suite covering validateRow (success/failure/advisory/timeout/fallback paths), listTables, and checkHealth with mocked global.fetch.
Mercury Webhook Tests
server/__tests__/webhooks-mercury.test.ts
Added end-to-end Vitest suite exercising authorization, envelope parsing, COA mapping, KV and DB dedup, advisory validation, and envelope-only acknowledgements with mocked dependencies.

Sequence Diagram

sequenceDiagram
    participant Client as Webhook Client
    participant Handler as Mercury Handler
    participant KV as KV Store
    participant ChittySchema as ChittySchema Service
    participant DB as SystemStorage (DB)
    participant Audit as ledgerLog
    
    Client->>Handler: POST /api/webhooks/mercury
    Handler->>Handler: Parse & validate envelope (Zod)
    Handler->>KV: Check eventId exists (7-day TTL)
    alt Duplicate Event
        KV-->>Handler: Found
        Handler->>Client: 202 duplicate: true
    else New Event
        KV->>KV: Store eventId
        Handler->>Handler: Compute COA code & confidence
        Handler->>ChittySchema: POST /api/validate (advisory)
        alt ChittySchema Responds
            ChittySchema-->>Handler: valid/invalid result
            Handler->>Handler: Log warnings if invalid
        else ChittySchema Fails
            Handler->>Handler: Set schemaAdvisory: true
        end
        Handler->>DB: Check externalId exists
        alt Transaction Exists
            DB-->>Handler: Existing transactionId
            Handler->>Client: 202 with existing ID
        else New Transaction
            Handler->>DB: createTransaction (with metadata)
            DB-->>Handler: Created transactionId
            Handler->>Audit: Emit ledgerLog entry
            Handler->>Client: 201 with transactionId, COA, confidence
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #84: Introduced database columns (suggested_coa_code, classification_confidence, chart_of_accounts table) that directly support the COA code computation and classification metadata now persisted by the Mercury webhook in this PR.

Poem

🐰 A schema so new, worker-aligned and fine,
No more env-at-load, just functions that shine.
Advisory, forgiving when services are shy,
We validate gently and let transactions fly!
KV dedup, DB dedup, COA codes with grace,
Mercury's webhook has found its new place.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the two main changes: Mercury webhook enhancement with real-time classification and integration with ChittySchema service.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mercury-webhook-classification

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
chittyfinance 5f7ad4f Apr 10 2026, 11:30 PM

@chitcommit chitcommit merged commit 5e969e2 into main Apr 10, 2026
12 of 13 checks passed
@chitcommit chitcommit deleted the feat/mercury-webhook-classification branch April 10, 2026 23:31
@claude
Copy link
Copy Markdown

claude bot commented Apr 10, 2026

Code Review — PR #90: Mercury webhook real-time classification + ChittySchema integration

Overall this is solid, well-scoped work. The fall-open philosophy is correctly implemented, the dual-dedup strategy is clever, and the test coverage is thorough. A few issues worth addressing before merge.


Bugs / Issues

1. KV dedup write races the DB write — KV is written before the duplicate check (server/routes/webhooks.ts)

await kv.put(dedupKey, JSON.stringify(rawBody || {}), { expirationTtl: 604800 });
// ... then later ...
const dupRow = await storage.getTransactionByExternalId(externalId, tx.tenantId);

The KV entry is committed on line ~125, but if the DB write fails (network blip, constraint error, etc.) the event is permanently swallowed — a retry will hit the KV dedup and return 202 duplicate without ever persisting the transaction. The KV write should happen after the DB write succeeds, or at minimum the KV entry should only be written after confirming the DB row was created.

2. envelope-only events still write to KV unnecessarily

The KV dedup fires before the if (!tx) early return, so every account.created / webhook.ping / etc. consumes a KV write + 7-day entry even though we don't care about deduping them. This is minor but will inflate KV usage over time and add unnecessary latency to every non-transaction event.

3. mercuryWebhookEnvelopeSchema doesn't enforce the presence of id/eventId at schema level — but then rejects with a different code if both are absent

The envelope schema marks both id and eventId as .optional(), but the next check:

if (!eventId) {
  return c.json({ error: 'missing_event_id' }, 400);
}

returns missing_event_id (400) which is correct — but the error surface is split between invalid_envelope and missing_event_id. Consider making at least one of id/eventId required at the Zod level so envelope parse failures are consolidated into a single invalid_envelope code. (Or keep as-is and document it explicitly.)

4. rawBody is stored in KV but rawBody could be null (line ~125)

const rawBody = await c.req.json().catch(() => null);
// ...
await kv.put(dedupKey, JSON.stringify(rawBody || {}), ...);

If c.req.json() rejects, rawBody is null. The envelope parse will also fail, so you'd return 400 invalid_envelope before reaching the KV write — so this is actually safe in practice. But it's fragile: the catch(() => null) on the parse and the rawBody || {} guard obscure the dependency. Consider parsing body once and treating a null body as an immediately-invalid envelope.


Security

5. Service auth uses constant-time comparison?

if (!authHeader || !authHeader.startsWith('Bearer ') || authHeader.slice(7) !== expected) {

String inequality !== in JavaScript is not constant-time. For a service-to-service bearer token this is a low-severity concern (tokens aren't being guessed character-by-character from a webhook endpoint), but per OWASP timing-attack guidance, HMAC-compare or crypto.subtle.timingSafeEqual would be more correct. Something to address before this pattern gets copy-pasted to higher-value endpoints.


Design / Consistency

6. Confidence values are hardcoded as string literals — same values used in other classifiers?

const classificationConfidence = isSuspense ? '0.100' : '0.700';

The schema column is classification_confidence decimal(4,3). These literal values are also present elsewhere in the codebase (based on CLAUDE.md trust-path table). Consider exporting named constants from database/chart-of-accounts.ts or a shared classification-constants.ts so the confidence floor values are consistent across the ingest path and any future L2/L3 classifiers.

7. type derivation from amount sign duplicated across the call and the schema

type: tx.amount >= 0 ? 'income' : 'expense', // in createTransaction
// ...
type: tx.amount >= 0 ? 'income' : 'expense', // in the validateRow payload

Two identical expressions in the same function. Extract to a const txType = tx.amount >= 0 ? 'income' : 'expense' at the top of the transaction block.

8. getTransactionByExternalId takes a tenantId param — confirm this exists on SystemStorage

The CLAUDE.md notes that server/storage.ts is the legacy interface and server/storage/system.ts is the new one. The test mocks getTransactionByExternalId, but it's worth confirming the real SystemStorage in server/storage/system.ts has this method to avoid a runtime TypeError in production that the tests wouldn't catch.


Performance

9. createDb is called on every webhook request

const db = createDb(c.env.DATABASE_URL);
const storage = new SystemStorage(db);

Webhook routes skip storageMiddleware by design (no tenant context). If createDb opens a new connection pool per call this is a connection-leak concern under load. Either confirm createDb returns a singleton/pooled connection, or wire up a lightweight connection-per-request pattern that explicitly closes after the response.


Minor / Style

  • console.warn in validateRow inside chittyschema.ts is appropriate for a library that shouldn't throw, but the caller at the webhook level also logs its own warning on !schemaResult.ok. That's slightly redundant — the lib-level warn plus the caller warn means two log lines per validation failure. Consider suppressing one.
  • The comment // Advisory ChittySchema validation — never blocks the write, only logs. is helpful. Consider promoting it to a // ADVISORY: ... convention used consistently across all ChittySchema call sites so it's searchable.
  • The PR description says chart_of_accounts and classification_audit are not yet registered in ChittySchema. Worth adding a TODO: comment in webhooks.ts near the validateRow call noting this gap, so it surfaces in a future grep TODO pass.

Tests

The test suite is excellent — 10 cases covering auth, dedup, classification, and all the fall-open paths. One gap:

Missing test: DB createTransaction throws — what happens?

There's no test for the case where storage.createTransaction rejects (e.g. DB constraint violation, connection error). Currently the error would bubble up to Hono's default error handler as a 500, which is probably fine — but worth adding a case that verifies the response is a clean 500 (not a 201 with partial state) and confirms the KV entry was already written (illustrating the race condition in issue #1 above).


Summary

Category Count
Bugs 2 (KV-before-DB race, envelope-only KV writes)
Security 1 (timing-safe comparison)
Design 3 (confidence constants, duplicated expression, SystemStorage method)
Performance 1 (createDb per request)
Tests 1 gap

The KV-before-DB race (#1) is the most important to fix before merge — it can silently lose transactions in retry scenarios. Everything else is low-to-medium severity. Happy to discuss any of these.

🤖 Generated with Claude Code

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f7ad4ff48

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (existing) {
return c.json({ received: true, duplicate: true }, 202);
}
await kv.put(dedupKey, JSON.stringify(rawBody || {}), { expirationTtl: 604800 });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Write dedup key only after a successful DB write

The handler stores webhook:mercury:${eventId} in KV before any DB persistence, so a transient failure in getTransactionByExternalId/createTransaction will still mark the event as processed. On retry, the same event returns 202 duplicate and the transaction is permanently dropped unless manually replayed. This turns temporary infra errors into data loss for webhook ingest.

Useful? React with 👍 / 👎.

Comment on lines +173 to +174
const dupRow = await storage.getTransactionByExternalId(externalId, tx.tenantId);
if (dupRow) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make externalId dedup atomic to prevent duplicate inserts

Dedup is implemented as a read (getTransactionByExternalId) followed by a separate insert, which is race-prone under concurrent deliveries of the same webhook. Two requests can both observe no existing row and both insert. Since this code path relies on application-level checks (not an atomic upsert/constraint), duplicate transactions can be created during retries or parallel webhook delivery bursts.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds real-time COA suggestion at Mercury webhook ingest and introduces a Workers-native ChittySchema client for advisory validation, aligning the ingest path with the COA trust-path initiative and modernizing schema validation integration.

Changes:

  • Persist Mercury webhook transactions with L1 keyword-based suggestedCoaCode + confidence at ingest time.
  • Add server/lib/chittyschema.ts client (health, validate, list tables) with fall-open advisory behavior; remove stale /api/v1/* integrations.
  • Add test coverage for the new webhook ingest flow and ChittySchema client behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
server/routes/webhooks.ts Implements Mercury webhook envelope validation, KV+DB dedup, advisory ChittySchema validation, auto-classification, and persistence.
server/lib/chittyschema.ts Adds Workers-native ChittySchema client with fall-open validation semantics and utilities.
server/lib/chittyschema-validation.ts Removes legacy schema validation module using incorrect endpoints and Node env assumptions.
server/lib/chittyos-client.ts Removes deprecated ChittySchemaClient and factory export.
server/env.ts Adds optional CHITTYSCHEMA_URL override.
server/tests/webhooks-mercury.test.ts Adds end-to-end tests for webhook auth/validation/dedup/persistence/classification.
server/tests/chittyschema.test.ts Adds unit tests for ChittySchema client fall-open and response normalization behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +128 to +132
const existing = await kv.get(dedupKey);
if (existing) {
return c.json({ received: true, duplicate: true }, 202);
}
await kv.put(dedupKey, JSON.stringify(rawBody || {}), { expirationTtl: 604800 });
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KV idempotency key is written before any DB persistence/dedup happens. If the handler errors after this kv.put (DB outage, transient exception, etc.), subsequent retries with the same eventId will be treated as duplicates and the transaction will never be persisted. Consider only writing the KV marker after the DB insert succeeds (or storing an “inflight” marker with short TTL and overwriting it with a “done/transactionId” marker once persistence completes).

Copilot uses AI. Check for mistakes.
description: z.string(),
amount: z.number(), // signed: negative = expense
category: z.string().optional().nullable(),
postedAt: z.string(), // ISO 8601
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

postedAt is only validated as a generic string, but later it’s converted with new Date(tx.postedAt) for insertion into a timestamp column. A non-ISO or otherwise invalid value would pass zod validation and then produce an Invalid Date, likely causing a DB insert error. Consider validating postedAt as an ISO datetime (e.g. zod datetime()) and/or parsing/coercing it to a Date as part of validation.

Suggested change
postedAt: z.string(), // ISO 8601
postedAt: z.string().datetime({ offset: true }), // ISO 8601

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +100
// 2. KV-based idempotency (7-day TTL dedup window)
// 3. Parse + validate envelope with zod
// 4. If payload carries a transaction, persist it to the DB with an
// auto-suggested COA code (L0 → L1 keyword match at ingest)
// 5. Advisory validation against ChittySchema's FinancialTransactionsSchema
// — never blocks the write, only logs warnings
//
// Returns:
// 200 { received, duplicate? } — envelope-only events (no transaction)
// 201 { received, transactionId, suggestedCoaCode, schemaAdvisory? } — persisted tx
// 400 on validation failure (envelope or payload)
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return-code docs in this comment block don’t match the implementation: envelope-only events and KV duplicates return 202 (not 200), and the flow ordering described above also differs from the actual code (envelope parse happens before KV). Please update the comment to reflect the real behavior so operators don’t rely on incorrect status codes.

Suggested change
// 2. KV-based idempotency (7-day TTL dedup window)
// 3. Parse + validate envelope with zod
// 4. If payload carries a transaction, persist it to the DB with an
// auto-suggested COA code (L0 → L1 keyword match at ingest)
// 5. Advisory validation against ChittySchema's FinancialTransactionsSchema
// — never blocks the write, only logs warnings
//
// Returns:
// 200 { received, duplicate? } — envelope-only events (no transaction)
// 201 { received, transactionId, suggestedCoaCode, schemaAdvisory? } — persisted tx
// 400 on validation failure (envelope or payload)
// 2. Parse + validate envelope with zod
// 3. KV-based idempotency (7-day TTL dedup window)
// 4. If payload carries a transaction, persist it to the DB with an
// auto-suggested COA code (L0 → L1 keyword match at ingest)
// 5. Advisory validation against ChittySchema's FinancialTransactionsSchema
// — never blocks the write, only logs warnings
//
// Returns:
// 202 { received, duplicate: true } — duplicate event detected via KV
// 202 { received } — envelope-only events (no transaction)
// 201 { received, transactionId, suggestedCoaCode, schemaAdvisory? } — persisted tx
// 400 on validation failure (invalid envelope or transaction payload)

Copilot uses AI. Check for mistakes.
const DEFAULT_TIMEOUT_MS = 3000;

function baseUrlFromEnv(env: { CHITTYSCHEMA_URL?: string }): string {
return env.CHITTYSCHEMA_URL || DEFAULT_BASE_URL;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

baseUrlFromEnv() returns the env override verbatim. If CHITTYSCHEMA_URL is configured with a trailing slash, requests will go to ...//api/validate / ...//api/health. Consider normalizing by trimming a trailing slash (consistent with other clients in this repo) before appending paths.

Suggested change
return env.CHITTYSCHEMA_URL || DEFAULT_BASE_URL;
const baseUrl = env.CHITTYSCHEMA_URL || DEFAULT_BASE_URL;
return baseUrl.replace(/\/+$/, '');

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants