Files
accounted/lib/webhooks/public-events.ts
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

130 lines
4.9 KiB
TypeScript

/**
* The public webhook event catalogue: the single source of truth for which
* CoreEventType values the v1 webhook surface delivers.
*
* Everything that enumerates webhook events derives from this module, so the
* lists cannot drift apart again (they did: the handler delivered 28 events
* while the create schema and the docs page listed 24, so nobody could
* subscribe to the four reconciliation.* events):
*
* 1. lib/webhooks/handler.ts subscribes the fan-out handler to every event.
* 2. app/api/v1/companies/[companyId]/webhooks/route.ts builds the create
* schema's `event_type` enum from it, which is what the OpenAPI spec and
* the generated agent skill (skills/accounted-api) advertise.
* 3. lib/docs/content/webhooks.ts renders its "Event types" section from
* the groups below.
*
* Restricted to the resource-state-change events that are useful to external
* integrations; MCP telemetry events and internal-only flows (event_log
* writes, drafts, deletes) are deliberately excluded.
*
* Adding an event type here is a public-API change: add a changelog entry in
* lib/docs/content/changelog.ts. Additive changes do not bump API_V1_VERSION.
*
* This module must stay a leaf (type-only imports): it is imported by docs
* pages, route modules and the event-bus handler alike.
*/
import type { CoreEventType } from '@/lib/events/types'
export interface PublicWebhookEventEntry {
type: CoreEventType
/** Short docs prose, rendered after the event name on the docs page. */
description?: string
}
export interface PublicWebhookEventGroup {
/** Docs section heading, e.g. "Invoicing". */
title: string
/** Optional note rendered next to the heading (e.g. an extra scope requirement). */
note?: string
events: ReadonlyArray<PublicWebhookEventEntry>
}
export const PUBLIC_WEBHOOK_EVENT_GROUPS = [
{
title: 'Invoicing',
events: [
{ type: 'invoice.created', description: 'draft invoice created' },
{ type: 'invoice.sent', description: 'invoice marked sent (email delivered or external)' },
{ type: 'invoice.paid', description: 'invoice fully paid' },
{ type: 'credit_note.created', description: 'credit note issued' },
],
},
{
title: 'AP / suppliers',
events: [
{ type: 'supplier.created' },
{ type: 'supplier_invoice.registered' },
{ type: 'supplier_invoice.approved' },
{ type: 'supplier_invoice.paid' },
{ type: 'supplier_invoice.credited' },
{ type: 'supplier_invoice.uncredited', description: 'credit reversal' },
],
},
{
title: 'Customers',
events: [{ type: 'customer.created' }],
},
{
title: 'Bookkeeping',
events: [
{ type: 'journal_entry.committed', description: 'voucher posted (immutable from this point)' },
{ type: 'journal_entry.reversed', description: 'storno entry posted' },
{ type: 'journal_entry.corrected', description: 'rättelse via `correctEntry` (BFL 5 kap 5 §)' },
],
},
{
title: 'Transactions',
events: [
{ type: 'transaction.categorized', description: 'bank transaction assigned an account + tax code' },
{ type: 'transaction.reconciled', description: 'transaction matched to a posted entry' },
],
},
{
title: 'Reconciliation',
events: [
{
type: 'reconciliation.matched',
description: 'an outside item (bank row or skattekonto row) linked to a journal entry',
},
{ type: 'reconciliation.unmatched', description: 'a reconciliation link removed' },
{ type: 'reconciliation.signed_off', description: 'an account signed off as reconciled through a date' },
{ type: 'reconciliation.reopened', description: 'a sign-off reopened' },
],
},
{
title: 'Periods',
events: [
{ type: 'period.locked', description: 'fiscal period closed for writes' },
{ type: 'period.unlocked', description: 'fiscal period reopened' },
{ type: 'period.year_closed', description: 'full year-end procedure complete' },
],
},
{
title: 'Payroll',
note: 'requires `payroll:read` scope alongside `webhooks:manage`',
events: [
{ type: 'salary_run.created' },
{ type: 'salary_run.approved' },
{ type: 'salary_run.booked', description: 'journal entries posted' },
{ type: 'agi.generated', description: 'AGI XML produced' },
],
},
{
title: 'Documents',
events: [{ type: 'document.uploaded' }],
},
] as const satisfies ReadonlyArray<PublicWebhookEventGroup>
/** Union of every deliverable event type, narrowed from the catalogue. */
export type PublicWebhookEventType = (typeof PUBLIC_WEBHOOK_EVENT_GROUPS)[number]['events'][number]['type']
/**
* Flat, ordered list of every deliverable event type. Pass straight to
* `z.enum(...)` or `new Set(...)`.
*/
export const PUBLIC_WEBHOOK_EVENTS: ReadonlyArray<PublicWebhookEventType> = PUBLIC_WEBHOOK_EVENT_GROUPS.flatMap(
(group) => group.events.map((event) => event.type),
)