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
}
]
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