Business requirement OM9 · local

Integration guide for IDHub teams

The minimum contracts required to push source profiles and consume unified customer accounts in the selected realm.

1

Push source profiles

POST raw source data to the provider-specific event-receiver URL.

POST /event-receiver/{realmId}/{providerId}
2

IDHub resolves and mixes

Provider matching and account-data-mapping resources determine the target account.

core.customerId · core.gigyaId · core.personalId
3

Query unified accounts

Use realm-scoped Profile API GraphQL queries. The returned ID is the stable downstream key.

searchAccounts · account(id)

Ingestion contract

Provider order
  1. es-accounts
  2. es-employees
  3. es-contacts
  4. es-engagements
  5. es-trainings
Event body
{
  "id": "<source natural id>",
  "data": "<raw source record>"
}
Realm context

Use the realm UUID in the event-receiver path and the X-Realm header for Profile API queries.

Mandatory pharmacy relationship procedure

A pharmacy CRM identifier is an external lookup value, not an IDHub account reference. The Mesh SIA must complete this sequence for every contact relationship before posting the contact.

1. Push organizations

POST every pharmacy to /event-receiver/{realmId}/es-accounts before its contacts.

2. Wait until searchable

Organization ingestion is asynchronous. Retry the Profile API lookup until the account is indexed, using a bounded retry policy.

3. Resolve the CRM ID

Query searchAccounts by core.orgCrmId and require exactly one organization hit.

4. Enrich the relationship

Copy hits[0].ID to relationships[].idhubAccountId. Keep the CRM ID only as sourceCrmId for audit and reconciliation.

5. Push the contact

POST to /event-receiver/{realmId}/es-contacts only after every pharmacy relationship has an idhubAccountId.

6. Verify the person account

Query the person account and confirm extra.relationships[].idhubAccountId opens the expected organization account.

Organization lookup variables
{
  "query": {
    "filter": {
      "path": "core.orgCrmId",
      "op": "eq",
      "val": "<pharmacy CRM identifier>"
    }
  },
  "limit": 1
}
Required relationship payload
{
  "role": "workplace",
  "idhubAccountId": "<searchAccounts.hits[0].ID>",
  "sourceCrmId": "<pharmacy CRM identifier>",
  "status": "active",
  "fromTime": "<source timestamp>"
}
Do not push an unresolved contact

Defer it and record the contact ID, pharmacy CRM ID, lookup attempts and failure reason. In the supplied sample, contacts reference 229 distinct pharmacy CRM IDs, while only 21 are present in the organization sample. The remaining 208 references require organization data before those contacts can satisfy this contract.

What happens if this is bypassed

No stable navigation

The person cannot reliably navigate to the IDHub pharmacy account from a raw CRM ID.

Every consumer owns a join

Downstream applications must repeatedly resolve source-specific identifiers themselves.

Segmentation becomes unreliable

Pharmacy-derived territory, methal and sell-in audiences cannot be evaluated consistently.

Source changes break links

CRM merges or identifier reassignment can silently point the person relationship nowhere.

Account query contract

query SearchAccounts($query: QueryInput, $limit: Int) {
  searchAccounts(query: $query, limit: $limit) {
    total
    hits {
      ID
      profile {
        core: keydata(key: "core") { content }
        meta: keydata(key: "meta") { content }
      }
    }
  }
}
Use server totals

searchAccounts.total is the environment account total. Do not infer totals from the number of hits in one page.

Configuration ownership

Profile providers, mappings and tags are Core API resources. They describe how Profile API behaves, but they are not Profile API responses. Runtime proof should use searchProfiles, searchAccounts and account.