Contracting & Hierarchies API Overview
Before a producer can sell a carrier's product, they have to be contracted for it — approved to sell that product, at agreed commission terms, in specific states. Keeping track of who is contracted for what, and at what rate, is the work this API automates.
Those contractual relationships also form a chain. A producer contracts with an agency, the agency sits below an IMO (Independent Marketing Organization), and the IMO connects to the carrier. Every commission payout, every "ready to sell" check, and every compliance review depends on knowing that chain accurately.
Contracting and Hierarchies are one API covering both: the relationships, and the tree they form. Your systems read them instead of someone looking them up by hand.
What you can get:
- Producers — the individuals and firms contracted in your organization
- Contracts and contract assignments — who is contracted to sell which product, and the current status of each
- Assignment changes — a dated record of every amendment, for reconciliation and audit
- Carriers, products, and commission levels — your contracting catalog and compensation structure
- Your distribution hierarchy — every producer and organization position, with its upline, commission level, states, and contract status — see Hierarchy Nodes
- The hierarchy as it looked on any past date — for answering "who was this producer's upline when this commission was earned?"
The v2 Surface
The customer-facing API is versioned under /v2 and is read-only — every endpoint is a GET. It is purpose-built for integrations: plain JSON responses, token pagination, updated_since filtering for incremental sync, and reference names inlined next to their IDs so no follow-up lookups are needed.
Contracting records are created and maintained in the AgentSync application; your integration consumes them.
Hierarchies are not a separate system layered on top. A hierarchy position is a contract assignment, and the tree is formed by the upline links between assignments. That is why the two share one base URL, one set of credentials, one scope model, and one set of response conventions — and why you need carrier, product, and contract assignment data before hierarchy data means anything.
v2 Endpoints
All v2 endpoints are read-only (GET), token-paginated, and scoped to the calling customer — a resource that doesn't exist or belongs to another customer returns 404, never 403.
| Endpoint | Description | Scope |
|---|---|---|
GET /v2/producers, GET /v2/producers/{producerId} | Producers (individuals and firms) in your organization. The detail view embeds the producer's bank accounts with account and routing numbers masked to ****{last4}. | contracting.producers.read |
GET /v2/contracts, GET /v2/contracts/{id} | Contracts sent from your organization | contracting.contracts.read |
GET /v2/contract-assignments, GET /v2/contract-assignments/{id} | Contract assignments, with product, commission level, and status names inlined | contracting.contractassignments.read |
GET /v2/contract-assignment-changes, GET /v2/contract-assignment-changes/{id} | Point-in-time assignment changes, carrying the change's status and the snapshot window explicitly | contracting.contractassignments.read |
GET /v2/assigned-carriers | Carriers assigned to your account — your account configuration in one call | contracting.contractassignments.read |
GET /v2/assigned-products | Products assigned to your account, with line-of-business and submission-method names inlined and annualization options embedded | contracting.contractassignments.read |
GET /v2/assigned-commission-levels | Commission levels assigned to you across all products, with product and carrier context | contracting.contractassignments.read |
GET /v2/.../available-uplines | Candidates eligible to be selected as an upline, scoped by product, organization, contract assignment, or assignment change | contracting.contractassignments.read |
GET /v2/.../available-upline-firms | The same candidate list narrowed to firms, scoped by product or contract assignment | contracting.contractassignments.read |
GET /v2/organizations/{organizationId}/responsible-upline-firms | Firms acting as a responsible upline in your organization, each with name, npn, and fein. Supports as_of. | contracting.contractassignments.read |
GET /v2/organizations/{organizationId}/hierarchies | Your distribution hierarchy as a flat list of nodes — see Hierarchy Nodes below. Supports as_of for point-in-time snapshots. | contracting.contractassignments.read |
v2 Response Conventions
List responses use a plain envelope — no HAL wrappers:
{
"items": [ { ... }, { ... } ],
"page": {
"size": 25,
"nextToken": "eyJ2IjoxLCJrIjp7Li4ufX0"
}
}
- Pagination: pass
page.nextTokenback aspage_token;nextTokenisnullon the last page.page_sizedefaults to 25 (values outside 1–250 are rejected with400, not clamped). See Pagination. - Incremental sync:
updated_since(RFC3339 UTC, e.g.2026-06-01T00:00:00Z) returns only records modified at or after that instant. Supported on the producers, contracts, contract-assignments, contract-assignment-changes, assigned-carriers, assigned-products, and assigned-commission-levels lists. Use your last sync time as a high-water mark. - Inlined names: related reference data appears as an
...Idplus a...Namecompanion (assignmentStatusId/assignmentStatusName) — no lookup round trips. - Field conventions: JSON fields are
lowerCamelCase; query parameters arelower_snake_case. Timestamps end inAt(RFC3339 UTC); dates end inOn(yyyy-MM-dd). - Errors: non-2xx responses return
{ "message": "...", "details": [ { "param": "...", "message": "..." } ] }.
Hierarchy Nodes
GET /v2/organizations/{organizationId}/hierarchies returns your distribution hierarchy — the upline/downline chain derived from contract assignments — as a token-paginated flat list of nodes. Each node is one position, keyed by contractAssignmentId. Requires the contracting.contractassignments.read scope.
{
"items": [
{
"contractAssignmentId": "660e8400-e29b-41d4-a716-446655440001",
"uplineContractAssignmentId": null,
"producerName": "Acme IMO",
"type": "FIRM",
"personId": null,
"firmId": "770e8400-e29b-41d4-a716-446655440002",
"npn": "1234567",
"productId": "aa0e8400-e29b-41d4-a716-446655440005",
"productName": "Term Life 10-Year",
"assignmentStatusId": "f19c9ba8-f954-484c-bbf5-442241b14935",
"assignmentStatusName": "Approved",
"commissionLevelId": "ee0e8400-e29b-41d4-a716-446655440010",
"commissionLevelName": "L1",
"commissionLevel": 100.00,
"stateAbbreviations": ["CA", "TX", "NY"],
"writingNumber": null,
"effectiveOn": "2026-01-15",
"expiresOn": null,
"active": true
}
],
"page": { "size": 1, "nextToken": null }
}
| Field | Description |
|---|---|
contractAssignmentId | The node's key — stable across amendments, and matches the contractAssignmentId in hierarchy.producer.* webhook events. |
uplineContractAssignmentId | The parent node's contractAssignmentId, or null for root nodes. |
producerName | The producer or organization at this position. |
type | What kind of producer holds this position — AGENT (an individual) or FIRM. Determines which of the two identifiers below is populated. |
personId | The individual at this position. Populated when type is AGENT, null when type is FIRM. |
firmId | The firm at this position. Populated when type is FIRM, null when type is AGENT. |
npn | The producer's National Producer Number. |
productId, productName | The carrier product this position covers. |
assignmentStatusId, assignmentStatusName | The position's current assignment status, name inlined. |
commissionLevelId, commissionLevelName, commissionLevel | The position's commission level, with name and numeric value inlined. |
stateAbbreviations | States this position is contracted for. |
writingNumber | Carrier-assigned writing number, if applicable. |
effectiveOn, expiresOn | The assignment's contractual effective and expiration dates. |
active | Whether this position is active. Computed over the tree — a node is inactive when its own status is inactive or any node above it is. |
A producer is a single entity discriminated by type, not two records — an individual is not stored as both a person and a firm. Whichever identifier does not apply is null.
personId and firmId are Identity API identifiers. Use them to fetch the producer's addresses, phones, bank accounts, and E&O policies without any lookup or mapping step — see Joining with the Contracting API.
Detecting firms: test
firmIdfor presence, notpersonIdfor null. These fields behave differently in REST and in webhooks. On this REST resource, aFIRMnode haspersonId: null. In thehierarchy.producer.*webhook events,personIdis always populated, withfirmIdappearing alongside it for firms. Logic that branches onpersonId == nullworks against REST and then silently classifies every firm as an individual when pointed at the event stream. CheckingfirmIdfor presence is correct in both.
Query parameters: as_of (yyyy-MM-dd, point-in-time snapshot — omit for the current state), page_token, and page_size.
Reconstructing the tree: identify root nodes (uplineContractAssignmentId == null), group the rest by uplineContractAssignmentId, then attach children to their parent keyed on contractAssignmentId. Fetch every page first — a child node can appear on a later page than its parent.
Point-in-time snapshots: pass as_of=yyyy-MM-dd to get the hierarchy as it existed on that date — the primary mechanism for commissions reconciliation ("who was this producer's upline on the transaction date?"). Omit it for the current state, and always supply an explicit as_of when querying for historical purposes.
Not yet available on a node: carrier context, partnership fields, and commission split percentages. If your integration needs these, contact support@agentsync.io so we can factor it into prioritization.
Files and Versioning
Contract packets carry implicit versioning — an initial version and a counter-signed version, surfaced as distinct states.
Uploaded files — those added through the file upload component during onboarding in Contracting or Hierarchies — have no explicit version control. They are versioned at the storage layer, but that history is not exposed through the API: there is no version list and no diff between versions.
Webhook events fire when a new file is loaded, so you can detect that a file changed even though you cannot retrieve its history.
Webhook Events
The API publishes events for contract, assignment, assignment-change, packet, producer-status, responsible-party, and hierarchy changes. See Contracting API Webhook Events for the full catalog with payload schemas.
Hierarchy lifecycle changes are pushed alongside the contracting events:
| Event | When It Fires |
|---|---|
hierarchy.producer.added | A producer is newly placed under an upline in a hierarchy |
hierarchy.producer.changed | An existing producer's position changes — upline and/or commission level. Includes a changedFields array. |
hierarchy.producer.relationship.terminated | A producer's hierarchy relationship ends — its contract assignment transitions into a terminal status |
Event payloads link to hierarchy nodes by contractAssignmentId, the same key the REST endpoint uses, so you can update a stored tree directly from an event. See the Webhooks Quick Start Guide to register an endpoint.
Entity Relationships
Carrier (name, naic, tin)
|
|-- Product (name, lineOfBusinessId/Name, submissionMethodId/Name)
|
|-- CommissionLevel (name, type, firstYearValue, level)
Contract (agencyOrganizationId + carrierOrganizationId + personId [+ firmId])
|-- assignmentStatusId/Name, active, effectiveDate, endDate
|-- displayNumber, parentContractId
|
|-- ContractAssignment (contract + product + commissionLevel)
|-- assignmentStatusId/Name
|-- commissionLevelId/Name, productId/Name
|-- uplineContractAssignment (id, displayNumber, firmName, personName)
|-- writingNumber, referralCodes[], effectiveOn, expiresOn
|-- responsiblePartyId, responsibleUplineFirmId
|-- userDefined1–10
|
|-- ContractAssignmentChange (point-in-time snapshot of a change)
|-- assignmentStatusId/Name (the change's own status)
|-- newCommissionLevelId/Name, newAnnualizationId
|-- effectiveOn, expiresOn (contractual terms)
|-- snapshotStartOn, snapshotEndOn (window this change was current)
|-- uplineContractAssignmentChange (id, displayNumber, firmName, personName)
|-- taskTypeName, contractPacketId
|-- userDefined1–10
Producer (type: AGENT or FIRM)
|-- personId (individuals) or firmId (firms)
|-- npn, name fields, organizationId/Name, roleName, statusId/Name
|-- divisions[], active
|-- bankAccounts[] (detail view only; numbers masked)
Use Cases
Insurance Carriers
- Synchronize your internal product catalog with AgentSync so contract assignments always reflect current offerings
- Automate producer onboarding by triggering invitations when a new agent appointment is approved
- Track assignment changes over time with
GET /v2/contract-assignment-changes?updated_since=...
Agencies and MGAs
- Read your account configuration — assigned carriers, products, and commission levels — in three calls
- Bulk-update contract assignments when onboarding a new producer cohort
- Keep commissions and downstream systems current with
updated_sincepolling plus webhook events
Technology Partners
- Integrate contracting data into agency management systems or producer portals
- Build automated compliance checkpoints using contract assignment status
- Correlate webhook events with REST records using shared identifiers
Commissions Platforms
- Determine the upline chain at transaction time — query the hierarchy with
as_ofset to the transaction date and walkuplineContractAssignmentIdlinks - Read the commission level on any node —
commissionLevelNameandcommissionLevelare inlined, so no second call is needed - Detect commission level or upline changes in real time via
hierarchy.producer.changed, and handle transfers and terminations viahierarchy.producer.relationship.terminated
Reconciliation and Audit
- Pull the full node list as of a reconciliation date, then diff against your local records on
contractAssignmentId - Compare
as_ofsnapshots across two dates to identify what changed in a window — there is no date-range filter - Correlate a node's
contractAssignmentIdback to the full record (GET /v2/contract-assignments/{id}) for complete audit trails
Getting Started
Start here: The Contracting & Hierarchies API Quick Start Guide walks through authentication, reading your account configuration, producers, assignments and hierarchy, reconstructing the hierarchy tree, point-in-time snapshots, resolving contact details from the Identity API, and incremental sync — with curl and Python examples throughout.
For a full list of endpoints, request/response schemas, and field constraints, see the interactive API Reference.