# Customer Credential Custom Headers - Implementation Tasks

## Overview

Implementation of the **Customer Credential Custom Headers** delivery slice.

This task plan is derived from review of the current branch implementation across:

- `backend/Kds.Ai.ApiService/Endpoints/AdminCustomerEndpoints.cs`
- `backend/Kds.Ai.ApiService/Services/CustomerManagementService.cs`
- `backend/Kds.Ai.ApiService/Services/CustomerEtlProvisioningService.cs`
- `backend/Kds.Ai.Data/Credentials/BrinkPosCredentialPayload.cs`
- `backend/Kds.Ai.Data/Credentials/CustomerCredentialEnvelopeFactory.cs`
- `backend/Kds.Ai.Integrations/BrinkPos/IBrinkCredentialReadService.cs`
- `backend/Kds.Ai.Integrations/BrinkPos/CustomerCredentialBackedBrinkCredentialReadService.cs`
- `backend/Kds.Ai.Integrations/BrinkPos/RepositoryBackedBrinkConnectionBindingService.cs`
- `backend/integrations/Kds.Ai.Integrations.BrinkPos/Models/BrinkConnectionDefinition.cs`
- `backend/integrations/Kds.Ai.Integrations.BrinkPos/Services/Facade/Clients/BrinkAuthHeaderScopeFactory.cs`
- `backend/tests/Kds.Ai.ApiService.Tests/Kds.Ai.ApiService.Tests/Endpoints/AdminCustomerEndpointsTests.cs`
- `backend/tests/Kds.Ai.ApiService.Tests/Kds.Ai.ApiService.Tests/Endpoints/AdminCustomerOnboardingFlowTests.cs`
- `backend/tests/Kds.Ai.ApiService.Tests/Kds.Ai.ApiService.Tests/Endpoints/BrinkPosManualEtlEndpointTests.cs`
- `backend/tests/Kds.Ai.ApiService.Tests/Kds.Ai.ApiService.Tests/Services/CustomerEtlProvisioningServiceTests.cs`
- `backend/tests/Kds.Ai.Integrations.Tests/BrinkPos/RepositoryBackedBrinkConnectionBindingServiceTests.cs`
- `backend/tests/Kds.Ai.Integrations.Tests/BrinkPos/BrinkPosDownstreamLiveTests.cs`
- `backend/tests/Kds.Ai.Integrations.Tests/BrinkPos/BrinkPosExtractRawLiveTests.cs`

The current branch supports structured customer credentials for Brink POS, but the payload only contains the fixed credential fields:

- `serviceUrl`
- `accessToken`
- `locationToken`
- `locationKey`
- `enabled`

The requested slice extends that payload with a dictionary of custom HTTP headers so each customer credential can carry source-specific header name/value pairs. Those headers must then be attached to facade requests whenever they are present.

The slice outcome is:

- admin customer payloads can accept and return a `customHeaders` dictionary inside `apiKeys[].payload`,
- persisted credential envelopes keep those headers together with existing Brink payload fields,
- custom header values are treated as secrets in the same way as token fields when returned from admin APIs,
- update flows preserve existing header values when a client sends redacted placeholders,
- repository-backed Brink credential binding exposes custom headers to the integration layer,
- the Brink facade adds all configured custom headers to outgoing requests in addition to existing auth headers,
- the design remains generic enough that other source types can follow the same pattern later.

## Source Documents

- repository analysis performed during this planning session
- current admin customer credential contract and tests
- current Brink credential binding and facade transport code

## Spec Name

This implementation spec should be referred to as **Customer Credential Custom Headers**.

## Fixed Decisions

- The new field is named `customHeaders` and lives inside `apiKeys[].payload`.
- `customHeaders` is a string-to-string dictionary keyed by the outbound HTTP header name.
- Header names are user-provided and may differ by source system.
- When custom headers are configured, the facade should always add them to outgoing requests for that credential.
- Existing Brink auth headers `AccessToken` and `LocationToken` remain unchanged and are still applied through the current authentication path.
- Custom header values must be treated as secrets in admin read responses.
- Update flows must support preserving existing stored custom header values when the client sends redacted placeholders for unchanged secrets.
- Empty or missing `customHeaders` means no additional headers are attached.
- Legacy `BrinkPosConnectionOptions` fallback remains supported as-is for this slice and does not gain custom-header support unless there is already a concrete config path for it.
- This slice is scoped to the current Brink POS integration path, but the payload shape should not block reuse by future source integrations.

## Scope

This task includes:

- extending the admin API request/response contract for Brink credentials with `customHeaders`,
- deciding validation and normalization rules for header names and values,
- extending the encrypted credential payload model and envelope handling,
- extending credential update logic to preserve redacted secret header values,
- extending credential-to-binding mapping and runtime connection definition models,
- attaching custom headers in the Brink facade transport request construction path,
- covering the new behavior with API, service, and integration tests.

In short, this slice ends when a customer credential can store source-specific custom headers and the Brink facade consistently sends them on downstream requests.

## Out of Scope

The following items are explicitly excluded from this task:

- redesigning the overall customer credential model for non-Brink source types,
- replacing the legacy `BrinkPosConnectionOptions` fallback contract,
- adding UI changes in Laravel unless another task explicitly requires them,
- changing manual ETL endpoint semantics beyond consuming the enriched stored credential,
- introducing a central cross-integration HTTP header framework.

---

## Task Checklist

### Phase 1: API Contract And Secret Semantics

- [ ] [TASK 1.1](./TASK1.1.md) - Define `customHeaders` payload contract and validation rules
- [ ] [TASK 1.2](./TASK1.2.md) - Define redaction and update-preservation rules for secret header values

### Phase 2: Persistence And Runtime Binding

- [ ] [TASK 2.1](./TASK2.1.md) - Extend credential payload storage, mapping, and binding models with custom headers
- [ ] [TASK 2.2](./TASK2.2.md) - Propagate custom headers through Brink connection resolution and facade transport

### Phase 3: Verification

- [ ] [TASK 3.1](./TASK3.1.md) - Add regression coverage for admin contract, secret preservation, and facade header emission

---

## Dependency Graph

```text
Phase 1 (API Contract And Secret Semantics)
    |-- 1.1 Define payload shape and validation rules
    +-- 1.2 Define redaction and update-preservation behavior <-- depends on 1.1

Phase 2 (Persistence And Runtime Binding)
    |-- 2.1 Storage, encryption payload, and binding model updates <-- depends on 1.1, 1.2
    +-- 2.2 Brink facade request propagation <-- depends on 2.1

Phase 3 (Verification)
    +-- 3.1 Regression coverage and live/manual validation <-- depends on 2.1, 2.2
```

---

## Recommended Session Order

| Session | Tasks | Description |
|---------|-------|-------------|
| 1 | 1.1, 1.2 | Lock the payload shape and secret-handling semantics before changing storage |
| 2 | 2.1 | Update persistence, admin mapping, and runtime binding models |
| 3 | 2.2 | Add custom headers to Brink facade requests |
| 4 | 3.1 | Add tests and verify end-to-end behavior |

---

## Key Design Decisions

### Keep The Shape Inside Credential Payload

The requested field belongs inside `apiKeys[].payload`, not as a sibling property of the credential envelope. That keeps per-connection transport metadata together with the rest of the downstream credential material.

### Treat Custom Header Values As Secrets

Custom header names are configuration metadata, but values can be secrets such as partner keys, tenant codes, or internal authorization tokens.

That means the admin read contract should not echo raw stored values back to the caller. The slice should extend the current redaction behavior beyond `accessToken` and `locationToken`.

### Preserve Existing Secret Values On Update

The current admin update flow already preserves `accessToken` and `locationToken` when the caller sends `[REDACTED]`.

The same rule should apply to custom header values so an edit of one field does not force users to re-enter every existing secret header.

### Propagate Through Existing Binding Path

The current runtime path is:

- encrypted customer credential entry,
- `CustomerCredentialEnvelopeFactory.ReadBrinkPayload`,
- `CustomerCredentialBackedBrinkCredentialReadService`,
- `RepositoryBackedBrinkConnectionBindingService`,
- `BrinkConnectionDefinition`,
- `BrinkAuthHeaderScopeFactory.CreateRequest`.

This slice should extend that path rather than bypass it.

### Prefer Minimal Genericity

The immediate runtime consumer is Brink POS, but header storage should be represented generically enough that future source integrations can reuse the same concept without another payload redesign.

That does not require building shared cross-integration abstractions yet. It only requires choosing a neutral payload field name and data shape.

### Validate Enough To Prevent Broken Requests

The plan should include pragmatic validation for malformed headers, for example:

- reject blank header names,
- reject duplicate names after any chosen normalization strategy,
- decide whether whitespace-only values are allowed or normalized,
- ensure invalid transport metadata is rejected before persistence.

Validation should stay small and deterministic. This slice does not need a large HTTP header policy engine.
