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.

EndpointDescriptionScope
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 organizationcontracting.contracts.read
GET /v2/contract-assignments, GET /v2/contract-assignments/{id}Contract assignments, with product, commission level, and status names inlinedcontracting.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 explicitlycontracting.contractassignments.read
GET /v2/assigned-carriersCarriers assigned to your account — your account configuration in one callcontracting.contractassignments.read
GET /v2/assigned-productsProducts assigned to your account, with line-of-business and submission-method names inlined and annualization options embeddedcontracting.contractassignments.read
GET /v2/assigned-commission-levelsCommission levels assigned to you across all products, with product and carrier contextcontracting.contractassignments.read
GET /v2/.../available-uplinesCandidates eligible to be selected as an upline, scoped by product, organization, contract assignment, or assignment changecontracting.contractassignments.read
GET /v2/.../available-upline-firmsThe same candidate list narrowed to firms, scoped by product or contract assignmentcontracting.contractassignments.read
GET /v2/organizations/{organizationId}/responsible-upline-firmsFirms acting as a responsible upline in your organization, each with name, npn, and fein. Supports as_of.contracting.contractassignments.read
GET /v2/organizations/{organizationId}/hierarchiesYour 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.nextToken back as page_token; nextToken is null on the last page. page_size defaults to 25 (values outside 1–250 are rejected with 400, 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 ...Id plus a ...Name companion (assignmentStatusId / assignmentStatusName) — no lookup round trips.
  • Field conventions: JSON fields are lowerCamelCase; query parameters are lower_snake_case. Timestamps end in At (RFC3339 UTC); dates end in On (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 }
}
FieldDescription
contractAssignmentIdThe node's key — stable across amendments, and matches the contractAssignmentId in hierarchy.producer.* webhook events.
uplineContractAssignmentIdThe parent node's contractAssignmentId, or null for root nodes.
producerNameThe producer or organization at this position.
typeWhat kind of producer holds this position — AGENT (an individual) or FIRM. Determines which of the two identifiers below is populated.
personIdThe individual at this position. Populated when type is AGENT, null when type is FIRM.
firmIdThe firm at this position. Populated when type is FIRM, null when type is AGENT.
npnThe producer's National Producer Number.
productId, productNameThe carrier product this position covers.
assignmentStatusId, assignmentStatusNameThe position's current assignment status, name inlined.
commissionLevelId, commissionLevelName, commissionLevelThe position's commission level, with name and numeric value inlined.
stateAbbreviationsStates this position is contracted for.
writingNumberCarrier-assigned writing number, if applicable.
effectiveOn, expiresOnThe assignment's contractual effective and expiration dates.
activeWhether 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 firmId for presence, not personId for null. These fields behave differently in REST and in webhooks. On this REST resource, a FIRM node has personId: null. In the hierarchy.producer.* webhook events, personId is always populated, with firmId appearing alongside it for firms. Logic that branches on personId == null works against REST and then silently classifies every firm as an individual when pointed at the event stream. Checking firmId for 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:

EventWhen It Fires
hierarchy.producer.addedA producer is newly placed under an upline in a hierarchy
hierarchy.producer.changedAn existing producer's position changes — upline and/or commission level. Includes a changedFields array.
hierarchy.producer.relationship.terminatedA 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_since polling 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_of set to the transaction date and walk uplineContractAssignmentId links
  • Read the commission level on any node — commissionLevelName and commissionLevel are 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 via hierarchy.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_of snapshots across two dates to identify what changed in a window — there is no date-range filter
  • Correlate a node's contractAssignmentId back 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.