# Note Synchronization

Learn how to integrate your practice management system with Askara to receive clinical notes automatically when they are generated from audio transcriptions.

## Overview

When a healthcare professional records an audio consultation in Askara, the system transcribes it and generates a clinical note using AI. Your system can receive these notes automatically via webhook or retrieve them by polling the API.

## Integration Flow

1. **Receive notification** - Via webhook (push) or polling (pull)
2. **Retrieve note** - Get note content and metadata via API
3. **Import to your system** - Save the note in your database


## Synchronization Methods

### Option 1: Webhooks (Recommended)

Webhooks provide real-time notifications when notes are generated. Askara sends an HTTP POST request to your endpoint.

**Advantages:**

- Real-time synchronization
- No polling overhead
- Efficient resource usage


#### 1. Register Your Webhook

**Endpoint:** `POST /webhooks`

[-> API Reference: Create Webhook](/apis/askara-symfony/askara-api/webhook/api_webhook_create)

```json
{
  "url": "https://your-system.com/api/askara/webhook",
  "subscriptions": "new_note"
}
```

**Fields:**

- `url` *(required)* - Public HTTP(S) URL of your endpoint
- `subscriptions` *(required)* - Comma-separated list of events, as a single string (for example `new_note,new_document`). Do not add spaces around the commas: unknown or malformed event names are ignored without an error.


Webhooks are registered per healthcare professional: the user who authorizes your application registers their own webhook, and receives events only for their own notes. Your OAuth2 client needs the `write:webhooks` scope.

The webhook is enabled on creation. If you register the same `url` again with the same account, Askara adds the new subscriptions to the existing webhook instead of creating a second one.

The response contains the webhook `uuid` and its `secret`. Store the `secret`: you need it to verify the signature of every request Askara sends you (see [Verify the Webhook Signature](#verify-the-webhook-signature)).

#### 2. Receive Webhook Events

When a note is generated, Askara sends a POST request to your webhook URL.

**Webhook Payload Structure:**

```json
{
  "events": [{
    "id": "note-uuid",
    "resource_type": "note",
    "action": "new_note",
    "metadata": {
      "note": {
        "uuid": "08b647ed-c4b4-48a6-8095-c907cf44c239",
        "content": "Patient presents with...",
        "type": "consultation_summary",
        "patient": "//api.askara.ai/patients/c4bcc8b5-b828-4dbc-a621-668540cb6812",
        "createdAt": "2026-02-24T10:30:00+00:00",
        "updatedAt": "2026-02-24T10:30:00+00:00",
        "validatedAt": null,
        "contentModified": false
      }
    },
    "links": [],
    "resource_metadata": {
      "user_id": "6ac32d28-7c31-4542-af58-43dd32d6b051",
      "organization_id": "73cf49ad-1f17-442e-be53-8ce0327a6063"
    }
  }],
  "meta": {
    "webhook_id": "your-webhook-uuid"
  }
}
```

`patient` is a reference to the patient resource (the identifier is the last segment), not an embedded object.

**Important:**

- Respond with a `2xx` status within 5 seconds: Askara considers any other answer, or no answer, as a failed delivery
- Askara sends each notification once and does not retry a failed delivery (see [Retry Behavior](#retry-behavior)), so also poll as described below if you cannot afford to miss a note
- Process the note asynchronously (don't block the response)
- Use the note `uuid` for deduplication


#### Verify the Webhook Signature

Every webhook request carries a `Webhook-Signature` header. Its value is the HMAC-SHA256 of the raw request body, keyed with your webhook `secret`, encoded as a lowercase hexadecimal string (no prefix).

To verify a request:

1. Read the raw request body exactly as received, before any JSON parsing.
2. Compute `HMAC-SHA256(secret, rawBody)` and encode it in hexadecimal.
3. Compare it with the `Webhook-Signature` header using a constant-time comparison. Reject the request if they differ.


```javascript
const crypto = require('crypto');

function isValidAskaraSignature(rawBody, signatureHeader, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const received = Buffer.from(signatureHeader || '', 'utf8');
  const computed = Buffer.from(expected, 'utf8');

  return received.length === computed.length && crypto.timingSafeEqual(received, computed);
}
```

The signature covers the body only: it contains no timestamp. Use the note `uuid` to ignore requests you have already processed.

#### 3. Retrieve Full Note Details (Optional)

If you need additional data not included in the webhook payload (e.g., associated audio or user information), fetch the full note via API.

**Endpoint:** `GET /notes/{uuid}`

[-> API Reference: Get Note](/apis/askara-symfony/askara-api/note/api_note)

### Option 2: Polling

If webhooks aren't feasible, poll periodically for new notes.

**Endpoint:** `GET /notes`

[-> API Reference: List Notes](/apis/askara-symfony/askara-api/note/api_notes)

**Useful query parameters:**

- `patient=<patient-uuid>` - Filter by patient
- `audio=<audio-uuid>` - Filter by source audio
- `createdAt[gte]=2026-02-17` - Notes created on or after this date (`gt`, `lt`, `lte` and `between` are also available)
- `order[createdAt]=desc` - Sort by newest first


**Recommended polling interval:** 30-60 seconds

## Retry Behavior

Askara tries to deliver each webhook **once**. If your endpoint does not answer within 5 seconds, is unreachable, or answers with a status code outside `2xx`, the delivery is recorded as failed and **Askara does not retry it automatically**.

To make sure you never miss a note:

- keep your endpoint available and answer quickly;
- poll `GET /notes` periodically (see [Polling](#option-2-polling)), for example with `createdAt[gte]` set to the date of the last note you imported, to catch the notes whose notification you missed.


## Required OAuth2 Scopes

To synchronize notes, your OAuth2 client must request this scope:

- `read:notes` - Retrieve note information
- `write:webhooks` - Register your webhook


[-> OAuth2 Guide](/guides/oauth2)

## Example Implementation

### Node.js Webhook Handler

```javascript
const express = require('express');
const app = express();

// Keep the raw body: the signature is computed on it (see "Verify the Webhook Signature")
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));

app.post('/api/askara/webhook', async (req, res) => {
  if (!isValidAskaraSignature(req.rawBody, req.get('Webhook-Signature'), process.env.ASKARA_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Respond quickly to avoid timeout
  res.status(200).send('OK');

  // Process asynchronously
  for (const event of req.body.events) {
    if (event.action === 'new_note') {
      const note = event.metadata.note;

      try {
        // Import note to your system
        await saveToSystem({
          uuid: note.uuid,
          content: note.content,
          patientUuid: note.patient.split('/').pop(), // "patient" is a reference ending with the patient uuid
          createdAt: note.createdAt
        });

        console.log(`Successfully imported note ${note.uuid}`);
      } catch (error) {
        console.error(`Failed to import note ${note.uuid}:`, error);
      }
    }
  }
});
```

## Best Practices

1. **Validate webhook signatures** - Check the `Webhook-Signature` header on every request (see [Verify the Webhook Signature](#verify-the-webhook-signature))
2. **Process asynchronously** - Don't block webhook responses while processing
3. **Handle duplicates** - Use note UUID to prevent duplicate imports
4. **Implement idempotency** - Same note UUID should produce the same result
5. **Log failures** - Include detailed error information for troubleshooting


## Troubleshooting

### Webhook Not Receiving Events

- Verify your webhook URL is publicly accessible
- Check that your endpoint responds with a `2xx` status within 5 seconds
- Check that the healthcare professional who owns the note registered a webhook with the `new_note` subscription: events are only sent to the webhook of the note's author
- Check that your endpoint is reachable from the internet: Askara does not call URLs that resolve to a private network
- If the problem persists, [contact our team](https://www.askara.ai/en/contact) with the note `uuid` and the approximate time of the event


### Notes Not Appearing

- Check OAuth2 token hasn't expired
- Verify you have the `read:notes` scope
- Confirm the audio transcription has completed before expecting a note


## Next Steps

- **[API Reference](/apis/askara-symfony/askara-api)** - Explore all endpoints
- **[Document Synchronization](/guides/document-synchronization)** - Synchronize validated documents
- **[OAuth2 Authentication](/guides/oauth2)** - Secure your integration
- **[Rate Limiting](/guides/rate_limiting)** - Understand API limits