Skip to content

AgenciesController

AgenciesController exposes the agency resource: a single agency by its six digit agency number, a producer-code search, the roster of agents currently appointed at an agency, and the one write endpoint in the service, which replaces an agency's contact block. The agency portal calls all four; SAIF.Claims.Intake uses the search and lookup endpoints to attribute a claim to a servicing agency; SAIF.Policy.RenewalNotice uses the lookup and roster endpoints through the SAIF.Agency.Api.Client package to name a servicing agent on each notice.

Field Value
Controller AgenciesController
Base AgencyApiBase : ApiController
Route prefix [RoutePrefix("v1/agencies")]
Source src/SAIF.Agency.Api/Controllers/v1/AgenciesController.cs
Controller-level authorization [SaifAuthorize(Roles = "AgencyRead")]
Error handling Global AgencyApiExceptionFilter; UpdateAgencyContact additionally catches StaleAgencyVersionException to attach the current version to the 409 body
Endpoints 4

GET /v1/agencies/{agencyNumber}

Returns a single agency, its producer codes, and its current contact block.

Field Value
HTTP verb GET
Resolved route /v1/agencies/{agencyNumber}
Route composition [RoutePrefix("v1/agencies")] + [Route("{agencyNumber}")]
Action GetAgency
Return type IHttpActionResult (Ok<AgencyDetailDto>)
Authorization AgencyRead
Idempotent Yes
Operation —

Parameters

Name In Type Required Default Binding Purpose
agencyNumber Route string Yes — Implicit Six digit agency number identifying the agency (BR-001)
includeTerminated Query bool No false [FromUri] Returns the agency even when its status is Terminated (BR-003)

Response

Field Value
DTO AgencyDetailDto (src/SAIF.Agency.Api.Core/Dtos/AgencyDetailDto.cs)
Media type application/json
Property Type Nullable Purpose
agencyNumber string No Six digit agency number
name string No Legal name of the agency
status string No Active, Suspended, or Terminated
producerCodes string[] No Producer codes assigned to the agency; empty for a terminated agency
appointedDate DateTime No Date the agency's first appointment took effect
terminatedDate DateTime Yes Date the agency was terminated; null while the agency is active or suspended
contact AgencyContactDto No Mailing address, contact email, and contact phone
version int No Row version used for optimistic concurrency on the contact update (BR-008)

Status codes

Status Condition Body
200 OK An agency with that number exists and is visible under the includeTerminated filter AgencyDetailDto
Error status Trigger Produced by Body
400 Bad Request agencyNumber is not exactly six digits (BR-001) Action, via AgencyApiBase.ValidationFailure ApiErrorDto with code AGENCY_NUMBER_FORMAT
404 Not Found No agency has that number, or the agency is terminated and includeTerminated is false (BR-003) Action, via AgencyApiBase.NotFoundForAgency ApiErrorDto with code AGENCY_NOT_FOUND
500 Internal Server Error Oracle unavailable or an unmapped exception escapes AgencyService AgencyApiExceptionFilter ApiErrorDto with code INTERNAL_ERROR and the correlation id

Authorization

The controller-level [SaifAuthorize(Roles = "AgencyRead")] applies to this action; there is no action-level attribute and no programmatic check inside the action. A caller without the role receives 401 Unauthorized from the filter before GetAgency runs. See Authentication and authorization.

Side effects and idempotency

Aspect Finding
Records written None
Downstream calls None
Files or messages produced None
Repeat-call behavior Identical response for identical input; the read runs in its own IUnitOfWork and holds no lock

Example

GET /agency/v1/agencies/104782 HTTP/1.1
Host: agencyapi.example.saif
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json

{
  "agencyNumber": "104782",
  "name": "Cascade Risk Partners LLC",
  "status": "Active",
  "producerCodes": ["CRP01", "CRP07"],
  "appointedDate": "2009-04-01T00:00:00Z",
  "contact": {
    "mailingAddress": {
      "line1": "2200 SW Water Ave",
      "line2": "Suite 310",
      "city": "Portland",
      "state": "OR",
      "postalCode": "97201"
    },
    "email": "service@cascaderisk.example",
    "phone": "5035550142"
  },
  "version": 14
}

GET /v1/agencies

Returns the agencies that hold a given producer code.

Field Value
HTTP verb GET
Resolved route /v1/agencies
Route composition [RoutePrefix("v1/agencies")] + [Route("")]
Action SearchAgencies
Return type IHttpActionResult (Ok<IEnumerable<AgencySummaryDto>>)
Authorization AgencyRead
Idempotent Yes
Operation —

Parameters

Name In Type Required Default Binding Purpose
producerCode Query string Yes — [FromUri] Producer code to search on; one to ten characters (BR-002)
activeOnly Query bool No true [FromUri] When false, suspended and terminated agencies are included (BR-003)

Response

Field Value
DTO AgencySummaryDto (src/SAIF.Agency.Api.Core/Dtos/AgencySummaryDto.cs), returned as a JSON array
Media type application/json
Property Type Nullable Purpose
agencyNumber string No Six digit agency number, usable with the lookup endpoint
name string No Legal name of the agency
status string No Active, Suspended, or Terminated
producerCode string No The producer code that matched the search
city string Yes Mailing address city, null when no contact block exists
state string Yes Mailing address state, null when no contact block exists

Status codes

Status Condition Body
200 OK The search completed; an empty array is a valid result AgencySummaryDto[]
Error status Trigger Produced by Body
400 Bad Request producerCode missing, empty, or longer than ten characters (BR-002) Action, via AgencyApiBase.ValidationFailure ApiErrorDto with code PRODUCER_CODE_REQUIRED
500 Internal Server Error Oracle unavailable, or the query exceeds the command timeout AgencyApiExceptionFilter ApiErrorDto with code INTERNAL_ERROR and the correlation id

Authorization

Inherited from the controller-level [SaifAuthorize(Roles = "AgencyRead")]. See Authentication and authorization.

Side effects and idempotency

Aspect Finding
Records written None
Downstream calls None
Files or messages produced None
Repeat-call behavior Identical response for identical input; results are not paged, and a producer code returns at most a handful of agencies

Example

GET /agency/v1/agencies?producerCode=CRP01&activeOnly=true HTTP/1.1
Host: agencyapi.example.saif
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "agencyNumber": "104782",
    "name": "Cascade Risk Partners LLC",
    "status": "Active",
    "producerCode": "CRP01",
    "city": "Portland",
    "state": "OR"
  }
]

GET /v1/agencies/{agencyNumber}/agents

Returns the agents currently appointed at an agency.

Field Value
HTTP verb GET
Resolved route /v1/agencies/{agencyNumber}/agents
Route composition [RoutePrefix("v1/agencies")] + [Route("{agencyNumber}/agents")]
Action GetAgentsForAgency
Return type IHttpActionResult (Ok<IEnumerable<AgentSummaryDto>>)
Authorization AgencyRead
Idempotent Yes
Operation —

Parameters

Name In Type Required Default Binding Purpose
agencyNumber Route string Yes — Implicit Six digit agency number whose roster is requested (BR-001)
asOfDate Query DateTime? No Current date [FromUri] Evaluates appointment status on this date instead of today (BR-004)

Response

Field Value
DTO AgentSummaryDto (src/SAIF.Agency.Api.Core/Dtos/AgentSummaryDto.cs), returned as a JSON array
Media type application/json
Property Type Nullable Purpose
agentNumber string No Agent number, usable with the agents endpoints
firstName string No Agent given name
lastName string No Agent surname
licenseNumber string No State producer license number
licenseState string No Two letter state code the license was issued in
appointmentEffectiveDate DateTime No Date the agent's appointment at this agency took effect
isPrimaryContact bool No True for the agent recorded as the agency's primary servicing contact

Status codes

Status Condition Body
200 OK The agency exists; an agency with no currently appointed agents returns an empty array AgentSummaryDto[]
Error status Trigger Produced by Body
400 Bad Request agencyNumber is not exactly six digits (BR-001), or asOfDate is not a parseable date Action, via AgencyApiBase.ValidationFailure; the date parse failure is produced by model binding ApiErrorDto with code AGENCY_NUMBER_FORMAT, or the framework's model state body for the date
404 Not Found No agency has that number Action, via AgencyApiBase.NotFoundForAgency ApiErrorDto with code AGENCY_NOT_FOUND
500 Internal Server Error Oracle unavailable or an unmapped exception escapes AgencyService AgencyApiExceptionFilter ApiErrorDto with code INTERNAL_ERROR and the correlation id

Authorization

Inherited from the controller-level [SaifAuthorize(Roles = "AgencyRead")]. A terminated agency's roster is readable by any caller holding that role; there is no data-level restriction limiting a caller to their own agency. See Authentication and authorization.

Side effects and idempotency

Aspect Finding
Records written None
Downstream calls None
Files or messages produced None
Repeat-call behavior Identical response for identical input on the same date; a request without asOfDate can change between days as appointments begin and end

Example

GET /agency/v1/agencies/104782/agents HTTP/1.1
Host: agencyapi.example.saif
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "agentNumber": "A0041627",
    "firstName": "Dana",
    "lastName": "Whitfield",
    "licenseNumber": "OR-884120",
    "licenseState": "OR",
    "appointmentEffectiveDate": "2018-07-01T00:00:00Z",
    "isPrimaryContact": true
  },
  {
    "agentNumber": "A0052918",
    "firstName": "Marcus",
    "lastName": "Ibarra",
    "licenseNumber": "OR-901554",
    "licenseState": "OR",
    "appointmentEffectiveDate": "2023-01-15T00:00:00Z",
    "isPrimaryContact": false
  }
]

PUT /v1/agencies/{agencyNumber}/contact

Replaces an agency's mailing address, contact email, and contact phone.

Field Value
HTTP verb PUT
Resolved route /v1/agencies/{agencyNumber}/contact
Route composition [RoutePrefix("v1/agencies")] + [Route("{agencyNumber}/contact")]
Action UpdateAgencyContact
Return type IHttpActionResult (StatusCode(HttpStatusCode.NoContent))
Authorization AgencyContactWrite
Idempotent Yes
Operation OP-001

Parameters

Name In Type Required Default Binding Purpose
agencyNumber Route string Yes — Implicit Six digit agency number whose contact block is replaced (BR-001)
request Body AgencyContactUpdateDto Yes — [FromBody] Replacement contact block and the caller's expected agency version

Request body

Field Value
DTO AgencyContactUpdateDto (src/SAIF.Agency.Api.Core/Dtos/AgencyContactUpdateDto.cs)
Media type application/json
Validation AgencyService.UpdateContact calls IContactValidator.Validate from SAIF.Framework.Validation; a null body is rejected by the action before the service is called
Property Type Required Constraints Purpose
email string Yes Deliverable address form, 255 characters maximum (BR-005) Address correspondence and renewal notices are sent to
phone string Yes Exactly ten digits, punctuation stripped before validation (BR-005) Contact telephone number
mailingAddress.line1 string Yes 60 characters maximum First line of the mailing address
mailingAddress.line2 string No 60 characters maximum Second line of the mailing address
mailingAddress.city string Yes 40 characters maximum Mailing address city
mailingAddress.state string Yes Two letter state code Mailing address state
mailingAddress.postalCode string Yes Five or nine digits Mailing address postal code
expectedVersion int Yes Must equal the agency's current version (BR-008) Optimistic concurrency token read from the lookup endpoint

Response

Field Value
DTO — (no response body)
Media type —
Property Type Nullable Purpose
X-Agency-Version int (response header) No The agency's new version, to be sent as expectedVersion on the next update

Status codes

Status Condition Body
204 No Content The contact block was replaced and the audit row written Empty; X-Agency-Version carries the new version
Error status Trigger Produced by Body
400 Bad Request agencyNumber is not six digits (BR-001), the body is absent, or email or phone fails validation (BR-005) Action for the route and null-body checks; AgencyApiExceptionFilter for AgencyValidationException raised by the service ApiErrorDto with code CONTACT_INVALID and one details entry per failing field
404 Not Found No agency has that number Action, via AgencyApiBase.NotFoundForAgency ApiErrorDto with code AGENCY_NOT_FOUND
409 Conflict The agency is Suspended or Terminated (BR-006) AgencyApiExceptionFilter, from AgencyStateConflictException ApiErrorDto with code AGENCY_NOT_ACTIVE
409 Conflict expectedVersion does not match the stored version (BR-008) AgencyApiExceptionFilter, from StaleAgencyVersionException caught and enriched by the action ApiErrorDto with code AGENCY_VERSION_STALE and the current version in details
500 Internal Server Error The unit of work fails to commit, or an unmapped exception escapes AgencyService AgencyApiExceptionFilter ApiErrorDto with code INTERNAL_ERROR and the correlation id

Authorization

This is the only action in the controller that carries its own attribute: [SaifAuthorize(Roles = "AgencyContactWrite")] replaces the controller-level AgencyRead requirement rather than adding to it, so a caller holding AgencyRead alone can read the agency but receives 401 Unauthorized here. The role is held by the agency portal's application identity and by the Agency Systems support group. See Authentication and authorization.

Side effects and idempotency

Aspect Finding
Records written AGENCY_CONTACT replaced in place and AGENCY.VERSION incremented; one AGENCY_CONTACT_AUDIT row inserted (data model) (BR-007)
Downstream calls None
Files or messages produced None
Repeat-call behavior Replaying the same request fails with 409 AGENCY_VERSION_STALE, because the first call incremented the version. Re-reading the agency and resubmitting with the new expectedVersion writes the same contact values and a second audit row.

Example

PUT /agency/v1/agencies/104782/contact HTTP/1.1
Host: agencyapi.example.saif
Content-Type: application/json

{
  "email": "service@cascaderisk.example",
  "phone": "(503) 555-0142",
  "mailingAddress": {
    "line1": "2200 SW Water Ave",
    "line2": "Suite 420",
    "city": "Portland",
    "state": "OR",
    "postalCode": "97201"
  },
  "expectedVersion": 14
}
HTTP/1.1 204 No Content
X-Agency-Version: 15

Sources

  • src/SAIF.Agency.Api/Controllers/v1/AgenciesController.cs
  • src/SAIF.Agency.Api/Controllers/AgencyApiBase.cs
  • src/SAIF.Agency.Api.Services/AgencyService.cs
  • src/SAIF.Agency.Api.Core/Dtos/AgencyDetailDto.cs
  • src/SAIF.Agency.Api.Core/Dtos/AgencySummaryDto.cs
  • src/SAIF.Agency.Api.Core/Dtos/AgentSummaryDto.cs
  • src/SAIF.Agency.Api.Core/Dtos/AgencyContactUpdateDto.cs
  • src/SAIF.Agency.Api.Data/Maps/AgencyMap.cs