Dentolize · Balance Invoice Webhooks & API Config Cleanup Walkthrough
On this pageBusiness viewTechnical view

API Config: one shape instead of three

Business view

Before this PR, every webhook a clinic configured had to be labeled with a Type: OTHER, NPHIES, or ZATCA. In practice, all three sent the exact same JSON payload — NPHIES and ZATCA were leftover scaffolding for integrations that were never actually built out differently. Nobody using the system benefited from picking one over another; it was just an extra required field on the form with no real effect.

This PR removes the Type dropdown entirely. Every webhook now behaves the way OTHER always did. This is a pure simplification — no clinic loses functionality, because NPHIES and ZATCA never did anything different from OTHER in the first place.

Technical view

What was deleted

Two files that produced payloads byte-for-byte identical to getOtherDataFromPayload are removed outright:

  • packages/server/src/apis/apiConfig/nphiesAPIs.js (deleted, was 74 lines)
  • packages/server/src/apis/apiConfig/zatcaAPIs.js (deleted, was 74 lines)

Both exported a getXDataFromPayload function with a switch on the same apiTypes values, and both bodies were verbatim copies of an older, smaller version of getOtherDataFromPayload (missing fields like email, gender, tooth, and referral_data that otherAPIs.js has picked up over time — another reason keeping them in sync was pure liability with no upside).

Dispatch collapses to a single call

packages/server/src/apis/apiConfig/mutationsApiConfig.js used to branch on an apiIntegrationTypes map (other / nphies / zatca) inside a getDataFromPayload switch. That switch and the type map are both deleted; handleCallApi now calls getOtherDataFromPayload unconditionally:

packages/server/src/apis/apiConfig/mutationsApiConfig.js:40-41

if (api.url && api.apis && api.apis.includes(type)) {
  const data = getOtherDataFromPayload({ type, payload, company })
  ...

Schema, validation, and UI all drop the type field

  • packages/server/src/inputs.graphql:758-763APIConfigObj.type: String!

is removed from the input type. The stored shape for each webhook row is now just { id, url, secret, apis }.

  • packages/server/src/permissions/inputRules.js:3350-3369

(updateAPIConfigInput) — the Yup validation for each row no longer validates a type field (formerly type: yup.string().max(100)), since it's no longer part of the input.

  • packages/clinic-web/src/components/dashboard/settings/Account/APIConfig.js

the apiTypes constant (the three { value, label } options feeding the Type <Select>) and the entire <Col> rendering that select are deleted. The submit handler is also tightened to only send known fields instead of spreading the whole form row:

``js apiConfig: values.input?.map(({ id, url, secret, apis }) => ({ id: id || uuidv4(), url, secret, apis })) ` (previously values.input?.map(s => ({ ...s, id: s.id || uuidv4() })), which would have silently kept sending a stale type` value from old form state).

What's unchanged

  • The apiEnabled company flag still gates the entire API Config tab (see

packages/clinic-web/src/components/dashboard/settings/SettingsIntegrations.js:44 and the server-side check in packages/server/src/resolvers/mutations/actions/company/updateAPIConfig.js:45). It's unrelated to this cleanup and stays exactly as it was.

  • The apiIntegrations list (which events a webhook can subscribe to) is

untouched apart from the two new balance entries — see Balance webhook events.

  • Each company can still register up to 10 webhook rows

(packages/server/src/permissions/inputRules.jsapiConfig: yup.array().max(10)), each with its own URL, optional HMAC secret, and event subscription list.