high priority high complexity api pending api specialist Tier 2

Acceptance Criteria

POST request is sent to the correct Bufdir API endpoint with the mapped BufdirApiRequest body serialised as JSON and Content-Type: application/json header
On HTTP 200/201, the bufdir_reference_number is extracted from the response body and returned in BufdirSubmissionResult.success
HTTP 400 (bad request) is mapped to BufdirSubmissionError.validationFailed with the API error message preserved
HTTP 409 (conflict/duplicate) is mapped to BufdirSubmissionError.duplicateSubmission and not retried
HTTP 422 (unprocessable) is mapped to BufdirSubmissionError.schemaRejected with field-level error details from response body
HTTP 500 and network errors trigger exponential backoff retry: initial delay 1s, multiplier 2x, max 3 retries, max delay 8s
After exhausting retries, BufdirSubmissionError.transientFailure is returned with attempt count and last error message
Request timeout is configurable (default 30s) and surfaces as BufdirSubmissionError.timeout on expiry
No Bufdir API credentials are included in request headers from the mobile client — submission is routed via Supabase Edge Function which injects credentials server-side
BufdirSubmissionResult is a sealed class (success / error variants) with no dynamic maps
Concurrent duplicate submissions for the same report_period_id are prevented by an in-flight guard returning the existing Future
All network errors are logged to the bufdir_export_audit_log with status 'submission_failed' and error detail

Technical Requirements

frameworks
Flutter
Dart
BLoC
Riverpod
apis
Bufdir Reporting API
Supabase Edge Functions (Deno)
data models
bufdir_export_audit_log
annual_summary
performance requirements
Request timeout configurable, default 30 seconds
Retry logic must not block the UI — run in an isolate or via async/await with no synchronous waits
Total retry window (3 retries with backoff) must not exceed 20 seconds
security requirements
Bufdir API credentials injected server-side in Supabase Edge Function — never present in mobile client request headers
TLS certificate pinning applied to Bufdir API endpoint
JWT from Supabase Auth included in Edge Function call for org-scoped credential lookup
Response body logged at debug level only — PII fields masked before logging
409 duplicate responses must not be retried to avoid creating duplicate government submissions

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Implement BufdirApiClient as a class injected with an http.Client (or Dio instance) so tests can substitute a mock. Use a RetryPolicy helper with configurable maxAttempts and backoff strategy — do not inline retry logic in the submit method. Route the actual HTTP call through a Supabase Edge Function (`functions/bufdir-submit`) which holds the real Bufdir credentials; the mobile client calls the Edge Function, not Bufdir directly. Use Dio's interceptor stack for logging (masked) and for injecting the Supabase JWT.

Model BufdirSubmissionResult with freezed sealed classes: `BufdirSubmissionResult` → `BufdirSubmissionSuccess(referenceNumber: String)` | `BufdirSubmissionError(type: BufdirErrorType, message: String, attempts: int)`. After each failed attempt, write an interim 'submission_failed' row to bufdir_export_audit_log so coordinators can see retry history.

Testing Requirements

Unit tests using flutter_test with a MockHttpClient (or mockito): (1) successful submission extracts reference number, (2) each error code (400, 409, 422, 500) maps to correct error variant, (3) three 500 errors exhaust retries and return transientFailure, (4) 409 is not retried, (5) network timeout surfaces as timeout error, (6) concurrent duplicate call returns same Future. Integration test against Bufdir sandbox environment (if available) covering happy-path and 409 conflict. All tests must run without real network access in CI.

Component
Bufdir API Client
service high
Epic Risks (2)
medium impact high prob dependency

Norse Digital Products has not yet completed API negotiations with Bufdir. If negotiations stall or Bufdir's API design diverges significantly from expectations, the API client may need substantial rework, or the epic may be blocked indefinitely.

Mitigation & Contingency

Mitigation: Implement the client against a locally defined stub of the expected Bufdir API schema. Isolate all Bufdir-specific schema mapping in a single adapter class so that changes to the actual API schema require changes in only one place. Keep the epic in 'interface-ready' status until real API credentials are available for integration testing.

Contingency: If API negotiations are not completed within the planned window, defer this epic without impact on any other epic — the PDF/CSV fallback path from Epics 1–4 delivers full standalone value. Mark the epic as blocked and resurface when negotiations conclude.

high impact low prob security

Bufdir API credentials stored in the application or edge function environment could be exposed through misconfigured secrets management, log leakage, or a compromised deployment pipeline, allowing unauthorised Bufdir submissions on behalf of the organisation.

Mitigation & Contingency

Mitigation: Store all Bufdir API credentials exclusively in Supabase Vault (or the integration credential vault component), never in client-side code or environment variables accessible to the Flutter app. Transmit credentials only from within the edge function, not from the Flutter client. Implement credential rotation support from the outset.

Contingency: If a credential leak is detected, immediately revoke and rotate the affected API credentials through Bufdir's credential management portal, audit submission logs for any unauthorised calls, and notify Bufdir's technical contact per the API agreement's security incident clause.