> ## Documentation Index
> Fetch the complete documentation index at: https://developer.suki.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# E/M Coding Asynchronous Notifications

> Webhook endpoint for receiving asynchronous notifications when standalone E/M coding session processing completes

Use this endpoint to implement an HTTP endpoint in your application that receives Webhook (A mechanism that allows Suki to send real-time event notifications to your application's server.) requests from Suki.
This endpoint should be hosted by your application to receive notifications about **standalone E/M coding** session (A single, time-bound instance of Evaluation and Management coding for partner-supplied note content.) completion or failure.

> [!NOTE]
>
> * Host this endpoint on your server. Suki sends a **POST** request when standalone E/M coding completes or fails. On success, `additional_info.session_type` is **`EM_CODES`**, and `_links` includes `status` and `em_codes` retrieval links for the E/M `session_id` returned from [Generate E/M codes](https://developer.suki.ai/em-coding-api-reference/em-coding-sessions/generate.md).
>
> * Failure payloads have no `additional_info`, so match their `session_id` against the E/M Coding session IDs you saved. `encounter_id` is always an empty string.

Learn more about how webhooks work in [Notification webhook for partners](https://developer.suki.ai/documentation/webhook/overview.md). For every field in the success and failure payloads, see [E/M Coding webhook payload](https://developer.suki.ai/documentation/webhook/em-coding/payload.md). To read the codes from the links, see [Retrieve E/M Coding results](https://developer.suki.ai/documentation/webhook/em-coding/retrieve-results.md).

## Code examples

**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.

> [tab] Python
>
> ```python theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> import hashlib
> import hmac
> import json
>
> from flask import Flask, jsonify, request
>
> app = Flask(__name__)
> secret_key = "<partner_secret_key>"
>
> # E/M Coding session IDs saved from Generate E/M codes.
> em_session_ids = set()
>
>
> @app.route("/webhooks/notification", methods=["POST"])
> def handle_webhook():
>     raw_body = request.get_data(as_text=True)
>     timestamp = request.headers.get("generated-at", "")
>     expected_signature_hex = request.headers.get("X-API-Key", "")
>     computed_signature_hex = hmac.new(
>         secret_key.encode("utf-8"),
>         f"{timestamp}:{raw_body}".encode("utf-8"),
>         hashlib.sha256,
>     ).hexdigest()
>
>     if not expected_signature_hex or not hmac.compare_digest(
>         computed_signature_hex, expected_signature_hex
>     ):
>         return jsonify({"error": "Invalid signature"}), 401
>
>     try:
>         data = json.loads(raw_body)
>     except json.JSONDecodeError:
>         return jsonify({"error": "Invalid request"}), 400
>
>     additional_info = data.get("additional_info") or {}
>     session_type = additional_info.get("session_type")
>     status = data.get("status")
>
>     if session_type == "EM_CODES" and status == "SUCCESS":
>         session_id = data.get("session_id")
>         print(f"E/M coding session {session_id} completed")
>
>         links = data.get("_links") or {}
>         for link in links.get("em_codes") or []:
>             print(f"  E/M codes: {link.get('method')} {link.get('href')}")
>         for link in links.get("status") or []:
>             print(f"  Status: {link.get('method')} {link.get('href')}")
>
>         return jsonify({"message": "Notification received"}), 200
>
>     # Failure payloads have no additional_info, so match the session ID instead.
>     if status == "FAILURE" and data.get("session_id") in em_session_ids:
>         print(
>             f"E/M coding session {data.get('session_id')} failed: "
>             f"{data.get('error_code')} - {data.get('error_detail')}"
>         )
>         return jsonify({"message": "Failure notification received"}), 200
>
>     return jsonify({"message": "Ignored"}), 200
>
>
> if __name__ == "__main__":
>     app.run(port=3000)
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> import crypto from "crypto";
> import express from "express";
>
> const app = express();
> const secretKey = "<partner_secret_key>";
>
> // E/M Coding session IDs saved from Generate E/M codes.
> const emSessionIds = new Set<string>();
>
> app.post("/webhooks/notification", express.raw({ type: "application/json" }), (req, res) => {
>   const rawBody = Buffer.isBuffer(req.body) ? req.body.toString("utf8") : String(req.body ?? "");
>   const timestamp = String(req.headers["generated-at"] ?? "");
>   const expectedSignatureHex = String(req.headers["x-api-key"] ?? "");
>   const computedSignatureHex = crypto
>     .createHmac("sha256", secretKey)
>     .update(`${timestamp}:${rawBody}`, "utf8")
>     .digest("hex");
>
>   const signaturesMatch =
>     Boolean(expectedSignatureHex) &&
>     computedSignatureHex.length === expectedSignatureHex.length &&
>     crypto.timingSafeEqual(
>       Buffer.from(computedSignatureHex, "utf8"),
>       Buffer.from(expectedSignatureHex, "utf8"),
>     );
>
>   if (!signaturesMatch) {
>     return res.status(401).json({ error: "Invalid signature" });
>   }
>
>   let data: Record<string, any>;
>   try {
>     data = JSON.parse(rawBody);
>   } catch {
>     return res.status(400).json({ error: "Invalid request" });
>   }
>
>   const sessionType = data.additional_info?.session_type;
>
>   if (sessionType === "EM_CODES" && data.status === "SUCCESS") {
>     console.log(`E/M coding session ${data.session_id} completed`);
>
>     const links = data._links ?? {};
>     (links.em_codes ?? []).forEach((link: { method?: string; href?: string }) => {
>       console.log(`  E/M codes: ${link.method} ${link.href}`);
>     });
>     (links.status ?? []).forEach((link: { method?: string; href?: string }) => {
>       console.log(`  Status: ${link.method} ${link.href}`);
>     });
>
>     return res.status(200).json({ message: "Notification received" });
>   }
>
>   // Failure payloads have no additional_info, so match the session ID instead.
>   if (data.status === "FAILURE" && emSessionIds.has(data.session_id)) {
>     console.log(
>       `E/M coding session ${data.session_id} failed: ${data.error_code} - ${data.error_detail}`,
>     );
>     return res.status(200).json({ message: "Failure notification received" });
>   }
>
>   return res.status(200).json({ message: "Ignored" });
> });
>
> app.listen(3000, () => {
>   console.log("Webhook server listening on port 3000");
> });
> ```


## OpenAPI

````yaml POST /webhooks/notification/em-codes
openapi: 3.0.1
info:
  title: Suki Developer Platform
  description: >-
    REST and WebSocket APIs for the Suki Developer Platform. Authenticate with
    Login or Register to obtain a Suki access token, then integrate ambient
    clinical documentation, form filling, transcription, content rewrite, and
    reference metadata endpoints.
  contact: {}
  version: '1.0'
servers:
  - url: https://sdp.suki.ai
    description: >-
      Production base URL for Suki Developer Platform REST APIs. WebSocket
      endpoints use the same host with `wss://`.
security:
  - SukiTokenAuth: []
paths:
  /webhooks/notification/em-codes:
    post:
      tags:
        - /webhooks
      summary: Receive standalone E/M coding notifications (partner webhook)
      description: >-
        Specification for the webhook endpoint your application hosts to receive
        asynchronous notifications when standalone E/M coding completes or
        fails. Suki sends POST requests to your configured notification URL. On
        success, additional_info.session_type is EM_CODES, and _links includes
        status (/em-codes/{id}/status) and em_codes (/em-codes/{id}), relative
        to /api/v1. encounter_id is an empty string because standalone E/M
        coding sessions are not tied to an encounter. Failure uses the shared
        notification failure shape, which has no additional_info.
      requestBody:
        required: false
        description: Standalone E/M coding success or failure notification payload.
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/controllers.EMCodesSuccessNotification'
                - $ref: '#/components/schemas/controllers.FailureNotification'
            examples:
              success:
                summary: Success
                value:
                  status: SUCCESS
                  session_id: a953839a-ddcd-407d-b9b0-3ed4b6be4be2
                  encounter_id: ''
                  sessions:
                    - a953839a-ddcd-407d-b9b0-3ed4b6be4be2
                  additional_info:
                    session_type: EM_CODES
                    practice_id: your-practice-id
                  _links:
                    em_codes:
                      - href: /em-codes/a953839a-ddcd-407d-b9b0-3ed4b6be4be2
                        method: GET
                        name: a953839a-ddcd-407d-b9b0-3ed4b6be4be2
                        type: application/json
                    status:
                      - href: /em-codes/a953839a-ddcd-407d-b9b0-3ed4b6be4be2/status
                        method: GET
                        name: a953839a-ddcd-407d-b9b0-3ed4b6be4be2
                        type: application/json
                    contents: []
                    transcripts: []
                    structured_data: []
                    section_structured_data: []
                    diagnoses: []
                    orders: []
                    encounter_content: []
                    encounter_structured_data: []
                    encounter_section_structured_data: []
                    encounter_em_codes: []
                    encounter_diagnoses: []
                    encounter_orders: []
              failure:
                summary: Failure
                value:
                  status: FAILURE
                  session_id: 20965414-929a-4f71-a3e5-b92bec07d086
                  encounter_id: ''
                  error_code: ERROR_CODE_UNSPECIFIED
                  error_detail: Error during E/M code generation
      responses:
        '200':
          description: OK
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/controllers.BadRequestError'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/controllers.AuthenticationError'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/controllers.InternalServerError'
      security: []
components:
  schemas:
    controllers.EMCodesSuccessNotification:
      description: >-
        Success notification for an em-codes session. Includes status
        (/em-codes/{id}/status) and em_codes (/em-codes/{id}) links.
        additional_info.session_type is EM_CODES.
      type: object
      properties:
        _links:
          description: Links to associated resources.
          allOf:
            - $ref: '#/components/schemas/controllers.Links'
        additional_info:
          description: >-
            Partner-level context. session_type is EM_CODES; practice_id is
            included when resolved.
          allOf:
            - $ref: '#/components/schemas/controllers.WebhookAdditionalInfo'
        encounter_id:
          description: >-
            Always an empty string. Standalone E/M coding sessions are not tied
            to an encounter.
          type: string
          example: ''
        session_id:
          description: Id of the em-codes session.
          type: string
          example: a953839a-ddcd-407d-b9b0-3ed4b6be4be2
        sessions:
          description: Sorted list of em-codes sessions, oldest first.
          type: array
          items:
            type: string
          example:
            - a953839a-ddcd-407d-b9b0-3ed4b6be4be2
        status:
          type: string
          example: SUCCESS
    controllers.FailureNotification:
      description: >-
        Failure notification shared by ambient, form filling, and em-codes
        sessions.
      type: object
      properties:
        encounter_id:
          description: Id of the encounter to which the payload belongs.
          type: string
          example: 29de56bc-960a-4cd5-b18f-79a798d62874
        error_code:
          description: Error code.
          type: string
          enum:
            - ERROR_CODE_UNSPECIFIED
            - ERROR_CODE_NOTIFICATION_GENERATION_FAILURE
          example: ERROR_CODE_UNSPECIFIED
        error_detail:
          description: Details of the error, if any.
          type: string
          example: Error in transcription
        session_id:
          description: Id of the session that failed.
          type: string
          example: 20965414-929a-4f71-a3e5-b92bec07d086
        status:
          type: string
          example: FAILURE
    controllers.BadRequestError:
      description: Bad Request Response
      type: object
      properties:
        code:
          type: integer
          example: 400
        message:
          type: string
          example: invalid request
    controllers.AuthenticationError:
      description: Authentication Failure Response
      type: object
      properties:
        code:
          type: integer
          example: 401
        message:
          type: string
          example: invalid token
    controllers.InternalServerError:
      description: Internal Server Error Response
      type: object
      properties:
        code:
          type: integer
          example: 500
        message:
          type: string
          example: internal server error
    controllers.Links:
      description: >-
        Artifact links advertised on a success notification. Unused kinds are
        sent as empty arrays.
      type: object
      properties:
        contents:
          description: >-
            Non-cumulative content for each ambient session so far for the
            encounter.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        diagnoses:
          description: >-
            Diagnoses for each ambient session at
            /session/{id}/structured-data/diagnoses.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        em_codes:
          description: >-
            E/M codes for each ambient session, or the em-codes session result
            at /em-codes/{id}.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        encounter_content:
          description: >-
            Cumulative content of the encounter through the latest successful
            ambient session.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        encounter_diagnoses:
          description: >-
            Encounter-level diagnoses for the latest successful ambient session
            at /encounter/{id}/structured-data/diagnoses.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        encounter_em_codes:
          description: Encounter-level E/M codes for the latest successful ambient session.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        encounter_orders:
          description: >-
            Encounter-level orders for the latest successful ambient session at
            /encounter/{id}/structured-data/orders.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        encounter_section_structured_data:
          description: >-
            Section structured data of the latest successful ambient session for
            the encounter.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        encounter_structured_data:
          description: >-
            Structured data of the latest successful ambient session for the
            encounter.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        orders:
          description: >-
            Orders for each ambient session at
            /session/{id}/structured-data/orders.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        section_structured_data:
          description: >-
            Section structured data for each ambient session so far for the
            encounter.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        status:
          description: Status of each session of this flow so far for the encounter.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        structured_data:
          description: >-
            Structured data for each session of this flow. Ambient uses
            /session/{id}/structured-data; form filling uses
            /form-filling/session/{id}/structured-data.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
        transcripts:
          description: Transcripts of each ambient session so far for the encounter.
          type: array
          items:
            $ref: '#/components/schemas/controllers.Link'
    controllers.WebhookAdditionalInfo:
      description: >-
        Partner-level context on notification and CKG payloads. session_type
        distinguishes the flow when a partner serves every webhook from one URL.
      type: object
      properties:
        practice_id:
          description: Partner-facing practice identifier.
          type: string
          example: practice-xyz
        session_type:
          description: Flow the payload came from.
          type: string
          enum:
            - AMBIENT
            - FORM_FILLING
            - EM_CODES
            - CKG_INGESTION
          example: AMBIENT
    controllers.Link:
      type: object
      properties:
        href:
          type: string
          example: /session/a953839a-ddcd-407d-b9b0-3ed4b6be4be2/status
        method:
          type: string
          example: GET
        name:
          type: string
          example: a953839a-ddcd-407d-b9b0-3ed4b6be4be2
        type:
          type: string
          example: application/json
  securitySchemes:
    SukiTokenAuth:
      type: apiKey
      in: header
      name: sdp_suki_token
      description: >-
        Suki access token (`suki_token`) from Login or Register. Expires after
        one hour.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.