Skip to content

feat(facility): API keys and webhooks management for facility system … - #167

Merged
afeezorobsco-cyber merged 2 commits into
DocForum:mainfrom
Bolajiomo99:feat/api-keys-and-webhooks-management-for-facility
Oct 27, 2026
Merged

afeezorobsco-cyber merged 2 commits into
DocForum:mainfrom
Bolajiomo99:feat/api-keys-and-webhooks-management-for-facility

Conversation

@Bolajiomo99

Copy link
Copy Markdown
Contributor

feat(facility): API keys and webhooks management for facility system integrations

Closes #55

Branch: feat/api-keys-and-webhooks-management-for-facility
Commit: 22105e3
Phase: W4 — Facility core flow (PRD §4 facility integration/fulfillment surfaces)
Depends on: DocForum/docforum-core#125 (facility API keys with HMAC signing) and DocForum/docforum-core#126 (signed outbound webhooks). Neither has landed yet, so this is built against the documented assumed contracts recorded in ARCHITECTURE_ESSENTIALS.md → “Known assumptions — Phase W4”.


Summary

Labs and pharmacies can now integrate their own software without opening a support ticket:

  • API keys — list keys (name, prefix, scopes, created, last used), create a key and display its full secret exactly once with copy + warning, and revoke a key with confirmation.
  • Webhook endpoints — register a URL with event subscriptions, display the HMAC signing secret exactly once, send a test event, view recent deliveries with status/response/attempts, and redeliver failed or pending deliveries.
  • Owner-only access — the management UI is gated on facility ownership (owner-only notice for staff; spinner while membership is checked).
  • One-time secrets — full secrets never enter localStorage, sessionStorage, URLs, logs, or the React Query caches after the dialog closes.

The new page lives at /facility/integrations and is linked from the header nav and every facility tab bar.


Description / What changed and why

1. Types (src/types/models.ts)

Added a new “Phase W4 — API keys & webhooks” section mirroring the assumed docforum-core contract (hard rule 2 — no parallel frontend-only shapes):

  • FacilityApiKey
  • CreatedApiKey (only response carrying secret)
  • CreateApiKeyInput
  • WebhookEventType
  • WebhookEndpoint
  • CreatedWebhookEndpoint (only response carrying signingSecret)
  • CreateWebhookInput
  • WebhookDeliveryStatus
  • WebhookDelivery
  • RedeliverResponse

The full secret is deliberately absent from FacilityApiKey and the full signing secret is absent from WebhookEndpoint, so ordinary list responses cannot leak them.

2. Service layer (src/services/integrations-service.ts)

All calls go through apiFetch in api-client.ts — no direct fetch, no Stellar/escrow calls (hard rule 1):

  • listApiKeys, createApiKey, revokeApiKey
  • listWebhookEndpoints, createWebhookEndpoint, deleteWebhookEndpoint
  • listWebhookDeliveries, sendTestDelivery, redeliverWebhook

3. Hooks (src/features/integrations/hooks/useIntegrations.ts)

React Query queries/mutations namespaced by ['facility', facilityId, …]:

  • Queries: useApiKeys, useWebhookEndpoints, useWebhookDeliveries
  • Mutations: useCreateApiKey, useRevokeApiKey, useCreateWebhookEndpoint, useDeleteWebhookEndpoint, useSendTestDelivery, useRedeliverWebhook
  • Successful mutations invalidate only the relevant list/delivery query.
  • Create mutations use gcTime: 0, so the mutation result (which contains the one-time secret) is evicted as soon as the dialog observer is removed.

4. Feature UI (src/features/integrations/)

  • IntegrationsView — page header, integration docs link, owner-only access check.
  • ApiKeysPanel — active key table, revoked-key section, create/revoke actions.
  • CreateApiKeyDialog — name form → secret-once screen with warning + copy.
  • WebhooksPanel — endpoint cards with URL, active state, secret prefix, events, last delivery, send-test/recent-deliveries/delete actions.
  • CreateWebhookDialog — URL + event subscriptions → signing-secret-once screen with warning + copy.
  • WebhookDeliveries — recent deliveries table (event, status, response code/error, attempts, delivered time) with redelivery for failed/pending.
  • Integrations.module.css — responsive styles consistent with the existing facility CSS modules.
  • Integrations.test.tsx — 15 component tests (see below).

5. Routing and navigation

  • src/pages/FacilityIntegrationsPage.tsx — facility tab nav + <IntegrationsView />.
  • src/App.tsx — facility/integrations route declared before the facility/* catch-all, wrapped in RequireAuth allowedRoles={['facility']}.
  • src/components/Layout.tsx — “Integrations” header link for facility users.
  • FacilityDashboardPage, FacilityPaymentsPage, FacilityTeamPage, OrderVerifyPage — Integrations tab added to each facility tab bar.

6. Mock API (MSW)

  • src/mocks/fixtures.ts — seeded fake API keys, webhook endpoints, and deliveries (all example.test, clearly labelled “fake”).
  • src/mocks/handlers/integrations.ts — list/create/revoke keys, list/create/delete endpoints, list/test/redeliver deliveries. Create responses return the secret once; stored records omit it.
  • src/mocks/handlers.ts — handler registration.
  • src/mocks/handlers.test.ts — 5 new integration tests driven through the real service layer.

7. Documentation / roadmap


Security / edge-case handling

Concern How it is handled
Secret shown once Secret is rendered only in the creation dialog, with an explicit “will never be shown again” warning and copy button.
Secret left in memory Closing calls mutation.reset() and create mutations use gcTime: 0; tests assert the secret is absent from both the query and mutation caches after close.
Secret persisted Never written to localStorage, sessionStorage, URLs, or logs; a test asserts storage is clean.
Secret in list responses FacilityApiKey/WebhookEndpoint types and mock handlers omit full secrets — only prefixes are listed.
Owner-only Membership list is checked; staff see an owner-only notice and no panels; non-facility roles also see a notice. Access is denied if membership can’t be verified.
Revocation Confirmation required; revoked keys stay visible in a separate revoked section for audit, not hard-deleted.
Webhook URL Must parse as http(s) before submit is enabled.
Event subscription At least one event must be selected.
Test delivery “Send test” records a delivery and automatically reveals the delivery history.
Failed deliveries failed and pending rows expose “Redeliver”; successful rows do not.
Clipboard unavailable Copy failure is caught; the value remains selectable manually.
Role mismatch Route allows only facility; the view additionally checks ownership.

Tests

New component tests (src/features/integrations/components/Integrations.test.tsx) — 15 tests

  1. Renders the key list with prefix, created and last-used columns.
  2. Shows the key secret exactly once and drops it from the DOM on close.
  3. Drops the key secret from React Query caches after close.
  4. Revokes a key after confirmation.
  5. Does not revoke when confirmation is cancelled.
  6. Does not show a secret before creation.
  7. Renders webhook endpoints with events and signing-secret prefix.
  8. Shows the webhook signing secret once and drops it on close.
  9. Drops the webhook signing secret from React Query caches after close.
  10. Sends a test event and opens delivery history.
  11. Redelivers a failed delivery.
  12. Deletes an endpoint after confirmation.
  13. Renders the page heading and integration docs link for facility users.
  14. Shows an owner-only notice and hides panels for staff.
  15. Shows a notice for non-facility roles.

New MSW handler tests (src/mocks/handlers.test.ts) — 5 tests

  1. Creates an API key with a one-time secret not returned by list.
  2. Revokes an API key and keeps it in the list as revoked.
  3. Creates a webhook endpoint with a one-time signing secret.
  4. Sends a test delivery and redelivers a failed one.
  5. Deletes an endpoint and its deliveries.

Actual local verification

npm run typecheck
> tsc -b --noEmit
(no errors)

npm test
Test Files  41 passed (41)
     Tests  393 passed (393)

npm run build
✓ built in 1.22s

npm run check:dist
✓ no mock code in production bundle (4 files scanned)

GitHub Actions will run the same gates on this branch.


Screenshots

All captured in screenshots/ at desktop 1440×1000 and mobile 390×844.

Integrations page

Integrations desktop
Integrations mobile

Create API key

Create API key desktop
Create API key mobile

API key secret shown once

API key secret desktop
API key secret mobile

Create webhook endpoint

Create webhook desktop
Create webhook mobile

Webhook signing secret shown once

Webhook secret desktop
Webhook secret mobile

Recent deliveries

Deliveries desktop
Deliveries mobile


Definition of done mapping

  • Integrations page implemented (/facility/integrations)
  • API keys: list prefix/created/last used, create with one-time secret, revoke
  • Webhook endpoints: URL + events, one-time signing secret, send test, deliveries with status, redeliver
  • Integration docs link
  • Tests for one-time secret display and revoke flows
  • One-time secrets dropped from DOM, React Query caches and storage after dialog close
  • Owner-only access handling
  • Desktop and mobile screenshots for every new/changed screen
  • ROADMAP.md updated (status + changelog)
  • ARCHITECTURE_ESSENTIALS.md “Known assumptions — Phase W4” updated
  • npm run typecheck green
  • npm test green (393 tests)
  • npm run build green
  • npm run check:dist green (no mocks in production bundle)
  • CI green (GitHub Actions on this PR)

How to manually verify

npm ci
npm run dev:mock

Open:

http://localhost:5173/?previewRole=facility#/facility/integrations

Then:

  1. Create an API key → confirm the secret is shown once.
  2. Close the dialog → confirm the secret disappears and DevTools storage is empty.
  3. Revoke a key → confirm the confirmation prompt and revoked-key section.
  4. Add a webhook endpoint → confirm the signing secret is shown once.
  5. Send a test → confirm delivery history appears.
  6. Redeliver the failed fixture delivery → confirm it updates.
  7. Delete an endpoint → confirm the confirmation prompt.

Run the automated suite with:

npm run typecheck
npm test
npm run build
npm run check:dist

Architecture / repo-rule compliance

  • Hard rule 1: no docforum-escrow or Stellar calls from this repo — everything goes through docforum-core’s API.
  • Hard rule 2: all types live in src/types/models.ts; no parallel frontend-only entity shapes.
  • Shape: pages/ components/ features/ hooks/ services/ store/ types/ preserved.
  • Server state: React Query only; no persistence of server-derived data.
  • AGENTS.md: PR notes the Phase W4 facility core flow, screenshots included, ROADMAP.md updated.
  • Mock rule: MSW fixtures/handlers added and covered; production bundle checked.

Known assumptions / follow-ups

Because docforum-core#125 and #126 have not landed, the endpoint shapes are assumptions and are documented in ARCHITECTURE_ESSENTIALS.md:

  • GET/POST /facilities/:facilityId/api-keys
  • DELETE /facilities/:facilityId/api-keys/:keyId
  • GET/POST /facilities/:facilityId/webhooks
  • DELETE /facilities/:facilityId/webhooks/:endpointId
  • GET/POST /facilities/:facilityId/webhooks/:endpointId/deliveries and /test
  • POST /facilities/:facilityId/webhooks/:endpointId/deliveries/:deliveryId/redeliver
  • Event names: order.created, order.assigned, order.fulfilled, order.cancelled, payment.released, payment.refunded
  • Revocation keeps the key with revokedAt rather than hard-deleting it
  • Owner-only enforcement is expected server-side as well

Should the real contracts differ, the only places to reconcile are src/types/models.ts, src/services/integrations-service.ts, and the mock handlers.

Potential follow-up issues (not in scope for #55):

  1. Server-side docforum-core contract reconciliation once [Platform] Real-user Web Vitals monitoring (privacy-preserving) #125/[Platform] Search across my own records (appointments, orders, results, messages) #126 land.
  2. Edit/toggle webhook endpoints without delete-and-recreate.
  3. API key scope selection UI (currently the create flow sends an empty scope list = all scopes).
  4. Delivery payload/response-body detail view for debugging.
  5. Dedicated integration docs page (the current link points to docforum-core’s docs/api/README.md because no dedicated docs page exists yet).
  6. Pagination for large delivery histories.

@drips-wave

drips-wave Bot commented Oct 19, 2026

Copy link
Copy Markdown

@Bolajiomo99 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Bolajiomo99 Thanks for the PR. It can't be merged yet. To unblock it:

Merge conflicts: this branch conflicts with main. Please rebase onto the latest main (git fetch upstream && git rebase upstream/main), resolve the conflicts (often ROADMAP.md, ARCHITECTURE*.md or prisma/schema.prisma), and force-push.

Push to this same branch and CI will re-run. Once it's green and conflict-free, it'll be reviewed for merge.

@github-actions github-actions Bot added needs-rebase Has merge conflicts with main; the author needs to rebase and removed needs-rebase Has merge conflicts with main; the author needs to rebase labels Oct 20, 2026
@github-actions

Copy link
Copy Markdown

@Bolajiomo99 This PR now has merge conflicts with main, most likely because #170 (Feat/facility performance insights turnaround times) was just merged, so it can't be merged until it's rebased:

git fetch upstream            # upstream = https://github.com/DocForum/docforum-web.git
git rebase upstream/main
# resolve the conflicts, re-run the tests, then:
git push --force-with-lease

The usual culprits are shared docs like ROADMAP.md and ARCHITECTURE*.md. If the conflict is in the ROADMAP.md changelog, move your entry into a new file under changelog.d/ (see changelog.d/README.md) rather than resolving it in place, so it can't conflict again. CI re-runs when you push, and the needs-rebase label is removed automatically once the conflicts are resolved.

Automated message. It won't repeat unless the PR conflicts again after your next push.

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Bolajiomo99 CI is failing on this branch against the latest main, so it can't be merged yet. The errors from the latest run:

Frontend (typecheck + test + build)

src/features/integrations/components/ApiKeysPanel.tsx(6,15): error TS2305: Module '"../../../types/models"' has no exported member 'FacilityApiKey'.
src/features/integrations/components/ApiKeysPanel.tsx(107,39): error TS7006: Parameter 's' implicitly has an 'any' type.
src/features/integrations/components/CreateApiKeyDialog.tsx(4,15): error TS2305: Module '"../../../types/models"' has no exported member 'CreatedApiKey'.
src/features/integrations/components/CreateWebhookDialog.tsx(4,15): error TS2305: Module '"../../../types/models"' has no exported member 'CreatedWebhookEndpoint'.
src/features/integrations/components/CreateWebhookDialog.tsx(4,39): error TS2305: Module '"../../../types/models"' has no exported member 'WebhookEventType'.
src/features/integrations/components/Integrations.test.tsx(13,15): error TS2305: Module '"../../../types/models"' has no exported member 'CreatedApiKey'.
src/features/integrations/components/Integrations.test.tsx(13,30): error TS2305: Module '"../../../types/models"' has no exported member 'CreatedWebhookEndpoint'.
src/features/integrations/components/WebhookDeliveries.tsx(4,15): error TS2305: Module '"../../../types/models"' has no exported member 'WebhookDelivery'.

src/types/models.ts doesn't export the API-key and webhook types this PR uses any more. The additions to that file were probably dropped while resolving a conflict, so re-add them.

This PR also conflicts with main again, so please rebase first, then fix the above.

@Bolajiomo99
Bolajiomo99 force-pushed the feat/api-keys-and-webhooks-management-for-facility branch from ccda56d to 914e815 Compare October 25, 2026 15:38
@github-actions github-actions Bot added needs-rebase Has merge conflicts with main; the author needs to rebase and removed needs-rebase Has merge conflicts with main; the author needs to rebase labels Oct 25, 2026
@github-actions

Copy link
Copy Markdown

@Bolajiomo99 This PR now has merge conflicts with main, most likely because #157 (feat(platform): complete W7 API client, errors, PWA, and i18n) was just merged, so it can't be merged until it's rebased:

git fetch upstream            # upstream = https://github.com/DocForum/docforum-web.git
git rebase upstream/main
# resolve the conflicts, re-run the tests, then:
git push --force-with-lease

The usual culprits are shared docs like ROADMAP.md and ARCHITECTURE*.md. If the conflict is in the ROADMAP.md changelog, move your entry into a new file under changelog.d/ (see changelog.d/README.md) rather than resolving it in place, so it can't conflict again. CI re-runs when you push, and the needs-rebase label is removed automatically once the conflicts are resolved.

Automated message. It won't repeat unless the PR conflicts again after your next push.

@Bolajiomo99

Copy link
Copy Markdown
Contributor Author

Kindly review and merge

@github-actions github-actions Bot removed the needs-rebase Has merge conflicts with main; the author needs to rebase label Oct 26, 2026

@afeezorobsco-cyber afeezorobsco-cyber left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sweep: malware scan clean, CI green.

@afeezorobsco-cyber
afeezorobsco-cyber merged commit 6d36cf6 into DocForum:main Oct 27, 2026
3 checks passed
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.

[Phase W4] API keys and webhooks management for facility system integrations

2 participants