---
title: Maintain provider identity
summary: Persists provider identifier records (including National Provider ID values) and synchronizes them to Contact Manager after each successful database change.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.Provider
reference_type: package
operation_id: OP-PRV-06
trigger: Caller creates or retrieves a clsProviderIdentity, edits properties, and calls Save() or Delete()
---

# Maintain provider identity

`clsProviderIdentity` maintains alternate provider identifiers (for example, National Provider IDs) keyed by `ProviderIdentitySeqNum`. Every successful insert, update, or delete attempts a follow-up call to `clsIdentifierApi` to keep Contact Manager's identifier list in sync.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-PRV-06 |
| **Trigger** | Caller calls `NewProviderIdentity`/`GetProviderIdentity`, edits properties, and calls `Save()` or `Delete()` |
| **Inputs** | `ProviderLocationSeqNum`, `ProviderIdentityCode`, `IdentityValue`, `IdentityName` |
| **Outputs** | Persisted [PROVIDER_IDENTITY](../database-tables.md#provider_identity) row; Contact Manager `IdentifierDto` sync |
| **Primary components** | `clsProviderIdentity.Create`, `BeforeInsert/BeforeUpdate/BeforeDelete`, `FillIdentifier`, `clsIdentifierApi.AddIdentifier/UpdateIdentifier/RemoveIdentifier` |
| **Entry point** | `Business\SAIF.Business.Provider\clsProviderIdentity.vb` |

## Steps

1. **Create.** `Create()` assigns a new seq num via `GetNextSeqNum("PROVIDER_IDENTITY_SEQ")` and stamps the acting user.
2. **Retrieve.** `BeforeRetrieve`/`AfterRetrieve` read [PROVIDER_IDENTITY](../database-tables.md#provider_identity) by `PROVIDER_IDENTITY_SEQ_NUM` ([SQL-048](../sql/SQL-048-retrieve-provider-identity.md)).
3. **Validate before save.**
   - `ValidType`: hard error requiring a nonblank `ProviderIdentityCode` (`"Error: Identifier type is missing."`).
   - `MissingValue`: hard error requiring a nonblank `IdentityValue` (`"Error: Identifier value is missing."`).
   - [BR-010](../business-rules.md#br-010-national-provider-id-values-must-be-10-digits): `ValidValue` — only when `ProviderIdentityCode` is `NPI` or `ONPI` — requires the trimmed `IdentityValue` to be exactly 10 characters (`"Error: National Provider ID must be 10 digits in length."`).
4. **Insert.** `BeforeInsert` writes `PROVIDER_IDENTITY_SEQ_NUM`, `PROVIDER_LOCATION_SEQ_NUM`, `PROVIDER_IDENTITY_CODE`, `IDENTITY_VALUE` (uppercased before persistence), `IDENTITY_NAME`, and audit fields to [PROVIDER_IDENTITY](../database-tables.md#provider_identity) ([SQL-024](../sql/SQL-024-insert-provider-identity.md)).
   - `FillIdentifier()` builds `IdentifierDto`: `officialIDType = ProviderIdentityCode`, `officialIDValue = IdentityValue.ToUpper`, `offcialIDSeqNumber = ProviderIdentitySeqNum.ToString`, `providerNumber = clsProviderUtility.GetProviderByProviderLocSeqNum(...)`.
   - On a rows-affected value other than `1`: add `"Insert Failed"`.
   - On success: `MarkOld()`, then call `clsIdentifierApi.AddIdentifier(...)` against route `provider/{providerlocationseqnumber}/identifier`.
     - On API exception: serialize `IdentifierDto` to JSON, log via `Logging.clsLogError.CreateError(...)`, and add `ApiInsertError` — the database insert is not rolled back.*
5. **Update.** `BeforeUpdate` writes the same fields (uppercasing `IdentityValue` again) to [PROVIDER_IDENTITY](../database-tables.md#provider_identity) ([SQL-072](../sql/SQL-072-update-provider-identity.md)) with concurrency check `WHERE PROVIDER_IDENTITY_SEQ_NUM = :ProviderIdentitySeqNum AND UPDATE_NUM = :UpdateNum`.
   - On success: `MarkOld()`, then call `clsIdentifierApi.UpdateIdentifier(...)` against route `provider/{providerlocationseqnumber}/identifier`; API failure is logged and surfaced as `ApiUpdateError`.
6. **Delete.** `BeforeDelete` deletes the [PROVIDER_IDENTITY](../database-tables.md#provider_identity) row by `PROVIDER_IDENTITY_SEQ_NUM` ([SQL-094](../sql/SQL-094-delete-provider-identity.md)); `FillIdentifier()` is rebuilt before the delete call so the DTO reflects the record being removed.
   - On success: `MarkNew()`, then call `clsIdentifierApi.RemoveIdentifier(...)` against route `provider/{providerlocationseqnumber}/identifier/remove`; API failure is logged and surfaced as `ApiDeleteError`.

## Process flow

```mermaid
flowchart TD
	Start([Caller edits identity]) --> TypeVal{Type and value present?}
	TypeVal -->|No| Fail([Broken rules returned])
	TypeVal -->|Yes| NpiCheck{Code is NPI or ONPI?}
	NpiCheck -->|Yes, not 10 chars| Fail
	NpiCheck -->|Valid or not applicable| Persist[Insert/update/delete PROVIDER_IDENTITY]
	Persist --> Rows{RowsAffected = 1?}
	Rows -->|No| Contention([Insert Failed / DB contention error])
	Rows -->|Yes| Api[Call clsIdentifierApi Add/Update/Remove]
	Api --> ApiOk{API call succeeds?}
	ApiOk -->|Yes| Done([Save complete; Contact Manager synced])
	ApiOk -->|No| Logged([Error logged and surfaced;<br>DB change already committed])
```

## Failure and edge-case behavior

- API synchronization failures are logged and surfaced as an error message, but the database change is never rolled back, so the identity record can become out of sync with Contact Manager.*
- The NPI/ONPI length rule only fires for those two specific code values; other identity types accept any nonblank value.
- Identity values are always uppercased before both database and API persistence, so lookups by value should assume uppercase.

## Related documentation

- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Sources

- `Business\SAIF.Business.Provider\clsProviderIdentity.vb`
- `Business\SAIF.Business.Provider\Api\clsIdentifierApi.vb`
- `Business\SAIF.Business.Provider\clsProviderUtility.vb`
