Connex → API reference

Integrate with Connex

Push every verified speed meeting into your CRM. HMAC-signed, replay-protected, idempotent — the standard webhook security model.

API version 2026-04-18 Base URL https://api.medismo.in Delivery at-least-once

Overview

Every speed meeting that gets verified at a clinic auto-pushes from Connex to your CRM as a signed webhook. FieldVoice and a generic HTTPS webhook are available today. Veeva, Salesforce Health Cloud and Zoho adapters are available on request.

Most integrations are live in 30 minutes. Pick an adapter in your Connex panel, paste the secret we show you once, and you're done.

Authentication

Outbound webhooks: every payload is signed with HMAC-SHA256 using the shared secret shown in your Connex panel at integration creation time. You never send your Connex secret to us — Connex uses it to sign outbound payloads, and you use it to verify them.

Admin API (managing integrations programmatically) uses the standard Connex bearer token from POST /v1/auth/login.

Adapters

Pick the one that matches your CRM. The adapter handles the schema mapping; you don't have to.

AdapterStatusNotes
fieldvoice · FieldVoiceGAField schema mapping handled automatically.
webhook · Generic WebhookGAAny HTTPS endpoint. You own the receiver.
veeva · Veeva CRMPreviewAvailable on request — contact integrations@medismo.in.
salesforce-hc · Salesforce Health CloudPreviewAvailable on request — contact integrations@medismo.in.
zoho · Zoho CRMPreviewAvailable on request — contact integrations@medismo.in.

Verifying signatures

Every webhook carries an X-Connex-Signature header in this format:

t=<unix_seconds>,v1=<hex_hmac_sha256>

Compute HMAC-SHA256 over <t>.<rawBody> with your shared secret and compare the hex digest to v1 in constant time. Reject signatures more than 300 seconds old.

import crypto from 'node:crypto';

export function verifyConnex(rawBody, signatureHeader, secret) {
  // Header looks like: t=1713456000,v1=abc123...
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((p) => p.split('='))
  );
  const ts = parts.t;
  const sig = parts.v1;

  // Reject if older than 5 minutes (replay defence).
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
    throw new Error('Signature too old');
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex');

  // Constant-time compare.
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    throw new Error('Bad signature');
  }
  return JSON.parse(rawBody);
}

Event envelope

Every event lands as JSON in the same envelope. data varies per event type — see below.

{
  "id": "evt_8f3a2c1e9b",
  "type": "meeting.completed",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": { /* event-specific, see below */ }
}

Events

Connex publishes 10 event types across the meeting lifecycle and the MR roster. Subscribe to all of them or pick a subset in your Connex panel.

POST meeting.scheduled

A meeting was just booked.

Fires: When an MR creates a booking via /api/v1/scheduling/bookings.

Payload fields (data object)

FieldTypeDescription
booking_idstringConnex booking UUID
mr_idstringConnex MR profile UUID
doctor_idstringConnex doctor profile UUID
slot_startISO 8601Meeting window start (timezone-aware)
slot_endISO 8601Meeting window end
location_idstring|nullClinic location UUID, if any
purposestringproduct_detailing | sample_delivery | follow_up | introduction

Example envelope

{
  "id": "evt_56dhurknj9",
  "type": "meeting.scheduled",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "booking_id": "bk_8f3a2c1e9b",
    "mr_id": "mr_a1b2c3d4e5",
    "doctor_id": "dr_priya_sharma",
    "slot_start": "2026-04-19T10:30:00.000Z",
    "slot_end": "2026-04-19T10:30:00.000Z",
    "location_id": "loc_apollo_jp_nagar",
    "purpose": "product_detailing"
  }
}
POST meeting.confirmed

Doctor confirmed a pending booking.

Fires: When a doctor flips status pending → confirmed.

Payload fields (data object)

FieldTypeDescription
booking_idstringConnex booking UUID
confirmed_atISO 8601When the doctor confirmed

Example envelope

{
  "id": "evt_wd3vuvgwya",
  "type": "meeting.confirmed",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "booking_id": "bk_8f3a2c1e9b",
    "confirmed_at": "2026-04-19T10:30:00.000Z"
  }
}
POST meeting.arrival_verified

MR arrived at the clinic and scanned the QR.

Fires: On successful QR + GPS verification at the clinic.

Payload fields (data object)

FieldTypeDescription
booking_idstringConnex booking UUID
arrived_atISO 8601When the MR scanned in
gps_distance_metersnumberDistance from clinic centroid (already validated)
location_namestringClinic location name (may be empty)

Example envelope

{
  "id": "evt_o2tsqz6soc",
  "type": "meeting.arrival_verified",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "booking_id": "bk_8f3a2c1e9b",
    "arrived_at": "2026-04-19T10:30:00.000Z",
    "gps_distance_meters": 12,
    "location_name": "Apollo Hospital — JP Nagar"
  }
}
POST meeting.completed

Meeting was marked complete with outcome data.

Fires: When the doctor marks complete OR the MR picks "Met the doctor" after the QR scan.

Payload fields (data object)

FieldTypeDescription
booking_idstringConnex booking UUID
met_atISO 8601When the meeting was marked complete
completion_notesstring|nullFree-text notes captured at completion
samples_providedbooleanWhether physical samples were given
iei_providedbooleanWhether an Item of Educational Interest was given (UCPMP)
iei_valuenumber|nullINR value of the IEI (capped at ₹1000 by UCPMP)
ai_extractedobject|nullAI-extracted structured fields (products, tone, topics, key_takeaway). Populated by a follow-up job.

Example envelope

{
  "id": "evt_5ky77u45rl",
  "type": "meeting.completed",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "booking_id": "bk_8f3a2c1e9b",
    "met_at": "2026-04-19T10:30:00.000Z",
    "completion_notes": "Discussed Amlodipine 5mg efficacy data; handed 2 sample packs.",
    "samples_provided": true,
    "iei_provided": false,
    "iei_value": null,
    "ai_extracted": null
  }
}
POST meeting.no_show

MR was at the clinic but the meeting did not happen.

Fires: When the MR picks "No meeting" after the QR scan.

Payload fields (data object)

FieldTypeDescription
booking_idstringConnex booking UUID
scheduled_startISO 8601Original slot start
noted_atISO 8601When no-show was recorded
reasonstring|nullOptional reason chosen by the MR

Example envelope

{
  "id": "evt_mhqnfok1v6",
  "type": "meeting.no_show",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "booking_id": "bk_8f3a2c1e9b",
    "scheduled_start": "2026-04-19T10:30:00.000Z",
    "noted_at": "2026-04-19T10:30:00.000Z",
    "reason": null
  }
}
POST meeting.cancelled

Booking was cancelled.

Fires: When either party cancels a booking before completion.

Payload fields (data object)

FieldTypeDescription
booking_idstringConnex booking UUID
cancelled_bystringdoctor | mr | system
cancelled_atISO 8601When cancelled
reasonstring|nullOptional reason

Example envelope

{
  "id": "evt_2nbggnmprh",
  "type": "meeting.cancelled",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "booking_id": "bk_8f3a2c1e9b",
    "cancelled_by": "doctor",
    "cancelled_at": "2026-04-19T10:30:00.000Z",
    "reason": null
  }
}
POST meeting.compliance_flag

Connex AI flagged a UCPMP risk on the completion notes.

Fires: For every medium- or high-severity flag returned by the post-meeting AI extraction.

Payload fields (data object)

FieldTypeDescription
booking_idstringConnex booking UUID
flag_typestringpossible_gift | cap_risk | comparative_claim | off_label_suggestion | volume_anomaly
severitystringlow | medium | high
evidencestringShort quote from completion notes
ai_model_versionstringLLM model that produced the flag

Example envelope

{
  "id": "evt_nn4njreecv",
  "type": "meeting.compliance_flag",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "booking_id": "bk_8f3a2c1e9b",
    "flag_type": "cap_risk",
    "severity": "medium",
    "evidence": "Mentioned ₹2,000 honorarium during meeting.",
    "ai_model_version": "gemini-1.5-flash"
  }
}
POST doctor.rated_mr

Doctor rated an MR after a meeting.

Fires: When the doctor submits a rating (or re-rates).

Payload fields (data object)

FieldTypeDescription
doctor_idstringConnex doctor profile UUID
mr_idstringConnex MR profile UUID
booking_idstringConnex booking UUID
ratingstringworth | okay | not_worth
commentstring|nullOptional doctor comment

Example envelope

{
  "id": "evt_mtz1x6a7k4",
  "type": "doctor.rated_mr",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "doctor_id": "dr_priya_sharma",
    "mr_id": "mr_a1b2c3d4e5",
    "booking_id": "bk_8f3a2c1e9b",
    "rating": "worth",
    "comment": "Useful clinical insights on Amlodipine."
  }
}
POST mr.onboarded

A new MR was added to your team.

Fires: When a pharma admin adds a team member via /api/v1/company/team.

Payload fields (data object)

FieldTypeDescription
mr_idstringConnex MR profile UUID
namestringMR full name
territorystring|nullOptional territory label
company_idstringPharma company UUID
emailstring|nullMR login email
phonestring|nullMR phone number

Example envelope

{
  "id": "evt_rm34g1uyqx",
  "type": "mr.onboarded",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "mr_id": "mr_a1b2c3d4e5",
    "name": "Rahul Singh",
    "territory": "Bangalore South",
    "company_id": "co_apex_pharma",
    "email": "rahul@apexpharma.test",
    "phone": "+91 98765 43210"
  }
}
POST mr.deactivated

An MR was deactivated and can no longer book meetings.

Fires: When a pharma admin deactivates a team member.

Payload fields (data object)

FieldTypeDescription
mr_idstringConnex MR profile UUID
deactivated_atISO 8601When deactivated
reasonstring|nullOptional reason

Example envelope

{
  "id": "evt_x6spks5dej",
  "type": "mr.deactivated",
  "api_version": "2026-04-18",
  "created_at": "2026-04-19T10:30:00.000Z",
  "account_id": "acct_apex_pharma",
  "data": {
    "mr_id": "mr_a1b2c3d4e5",
    "deactivated_at": "2026-04-19T10:30:00.000Z",
    "reason": null
  }
}

Retries & idempotency

Connex retries failed deliveries up to 8 times with exponential back-off:

  1. Attempt 1 · 0s
  2. Attempt 2 · 30s
  3. Attempt 3 · 2 min
  4. Attempt 4 · 10 min
  5. Attempt 5 · 1 hour
  6. Attempt 6 · 4 hours
  7. Attempt 7 · 12 hours
  8. Attempt 8 · 24 hours

4xx responses (except 408 and 429) are treated as permanent failures and are not retried — assume your code or config is wrong, fix it, and replay the event from your panel.

Every delivery carries a unique Idempotency-Key header. Connex guarantees at-least-once delivery; you guarantee dedup. The simplest pattern: store the key in a unique index and treat conflicts as no-ops.

Errors

What we do when your endpoint returns various status codes:

StatusTreated asAction
2xxSuccessMark delivered. Stop.
408 / 429TransientRetry per schedule above.
4xx (other)PermanentMark failed. No retry. Visible in panel for manual replay.
5xxTransientRetry per schedule above.
Network / timeoutTransientRetry per schedule above.