Към основното съдържание
MediSend Developers

API v1 · 1.1.1

MediSend API

Заявки за файлове, подготовка на предавания и подписани известия към Вашата система.

Започнете в изолирана тестова среда

Създайте тестов ключ в конзолата. Той е валиден 30 дни и работи само в /api/sandbox/v1; ключовете ms_live_ се отхвърлят там, а ms_test_ се отхвърлят от реалния API. Синтетичната организация, пакетите и заявките се пазят в отделни таблици. Няма достъп до реални файлове, email/SMS, получатели или таксуване.

Отвори тестовата среда

GET /capabilities показва средата, правата и лимита. GET /handovers използва page и per_page (1–100). POST /handovers приема само scenario: delivered, notification_failed, scanner_quarantined или awaiting_recipient. Това са контролирани резултати, а не реално доставени съобщения или сканирани файлове. Не изпращайте медицинска информация. simulate_error: provider_unavailable връща HTTP 503 без запис на пакет.

TypeScript · Node.js
// Node.js / TypeScript — keep the credential in a secret store.
const origin = process.env.MEDISEND_API_ORIGIN!;
const token = process.env.MEDISEND_SANDBOX_TOKEN!;
if (!token.startsWith('ms_test_')) throw Error('Sandbox credential required');
const key = crypto.randomUUID(); // Retain this key when retrying this operation.
const response = await fetch(origin + '/api/sandbox/v1/handovers', {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json',
    'Idempotency-Key': key, Accept: 'application/json' },
  body: JSON.stringify({ scenario: 'delivered' }),
});
const result = await response.json();
if (!response.ok) throw Error(result.error?.code ?? 'request_failed');
console.log(result.data.id, result.data.synthetic); // Synthetic ID, never patient data.
// Same key + same scenario => HTTP 200 and replayed:true; changed scenario => 409.
PHP · cURL
<?php
// PHP 8.3+ — credentials are read from the environment, never committed.
$origin = rtrim(getenv('MEDISEND_API_ORIGIN'), '/');
$token = getenv('MEDISEND_SANDBOX_TOKEN');
if (!str_starts_with($token, 'ms_test_')) throw new RuntimeException('Sandbox key required');
$key = bin2hex(random_bytes(16)); // Save this for retries of this operation.
$curl = curl_init($origin.'/api/sandbox/v1/handovers');
curl_setopt_array($curl, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
  CURLOPT_TIMEOUT => 15, CURLOPT_HTTPHEADER => [
    'Authorization: Bearer '.$token, 'Content-Type: application/json',
    'Accept: application/json', 'Idempotency-Key: '.$key],
  CURLOPT_POSTFIELDS => json_encode(['scenario' => 'scanner_quarantined'], JSON_THROW_ON_ERROR)]);
$body = curl_exec($curl);
if ($body === false) throw new RuntimeException('Transport failed; retain the same key');
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
$result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
if ($status >= 400) throw new RuntimeException($result['error']['code'] ?? 'request_failed');
echo $result['data']['id'].PHP_EOL;

Ограничение: 60 заявки/минута за тестов ключ. Отговорите включват X-MediSend-Environment, X-Request-ID, X-RateLimit-Limit и X-RateLimit-Remaining; при 429 спазвайте Retry-After. Само handovers:read и handovers:write са допустими. Ключът Idempotency-Key е 8–200 знака и е ограничен до конкретния тестов ключ. Реалният API има отделните правила за 24-часова идемпотентност по-долу.

За подписано известие добавете собствен публичен HTTPS адрес с webhook.test и изпратете избрания синтетичен сценарий от тестовата среда. Използва се действителната опашка и HMAC подпис. HTTP отговорът и всеки опит се виждат в диагностиката, без тяло на отговора. За проверка на повторенията върнете временно 503, след това 204 от Вашия тестов receiver. Не използвайте реални клинични системи за симулацията.

OpenAPI за тестовата среда v1 ↓

Първа връзка

Създайте отделен ключ за всяка система с право integration:read. Копирайте го веднъж в хранилище за тайни.

Създай първия API ключ
Shell · bash / zsh
# Set the API origin for your environment.
export API_BASE_URL="https://api.medisend.bg"
# Read the credential without saving it in shell history.
read -r -s MEDISEND_API_TOKEN
export MEDISEND_API_TOKEN
export IDEMPOTENCY_KEY="$(uuidgen)"

API_BASE_URL е API адресът на средата Ви; локално използвайте адреса на Вашия backend. Поставете ключа при read и натиснете Enter. Запазете UUID за повторенията на същата заявка. Първо проверете възможностите на организацията:

curl
curl --fail-with-body "$API_BASE_URL/api/v1/capabilities" \
  -H "Authorization: Bearer $MEDISEND_API_TOKEN" -H 'Accept: application/json'

Създайте заявка за файлове

POST /api/v1/upload-requests създава активна заявка в текущата организация. Задължителни са title, name и email. Срокът по подразбиране е 7 дни. require_access_pin включва отделен PIN. Отговор 201 съдържа data.id, data.reference, data.status, public_url, token, access_code и access_pin. Доставете достъпа по одобрен сигурен канал; API не изпраща автоматично имейл. Следващият пример използва синтетични данни.

За създаване и проверка на заявката добавете upload_requests:write и upload_requests:read към ключа.

curl
curl --fail-with-body "$API_BASE_URL/api/v1/upload-requests" \
  -H "Authorization: Bearer $MEDISEND_API_TOKEN" \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --data '{"title":"Partner files","name":"Partner clinic","email":"[email protected]","requested_files_description":"PDF or imaging files","require_access_pin":true}'
JSON · 201
{"data":{"id":"<request-id>","reference":"UR-…","status":"active","created_at":"…","expires_at":"…"},"public_url":"<secure-url>","token":"<shown-once>","access_code":"<shown-once>","access_pin":"<shown-once>"}

Известия към Вашата система

В Настройки регистрирайте публичен HTTPS адрес и изберете събития. Копирайте подписващата тайна веднъж. request.created се задейства от действително създадена заявка. Другите събития са изброени по-долу. Тялото съдържа id (събитие), type, created_at и data.object с id/type и евентуален status. Не съдържа медицински файлове, пациентски идентификатори или достъп. Записвайте id атомарно за защита от дублиране; отговаряйте с 2xx след устойчиво приемане.

Проверка на подписа

Четете MediSend-Delivery и MediSend-Signature: t=<Unix секунди>,v1=<hex HMAC>. Подписът е HMAC-SHA256 на точните байтове timestamp.deliveryId.rawBody. Сравнете с constant-time проверка и отхвърлете време извън 5 минути. Не пресериализирайте JSON. При смяна на тайната v1 използва новата, v0 старата за 24 часа. Запазете raw body преди JSON middleware.

Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody is the untouched HTTP request body. Read headers case-insensitively.
const parts = Object.fromEntries(signatureHeader.split(',').map(p => p.split('=')));
if (!/^\d+$/.test(parts.t) || Math.abs(Date.now()/1000 - Number(parts.t)) > 300) throw Error('timestamp');
if (!/^[a-f0-9]{64}$/.test(parts.v1)) throw Error('signature');
const expected = createHmac('sha256', signingSecret)
  .update(parts.t + '.' + deliveryId + '.').update(rawBody).digest();
if (!timingSafeEqual(expected, Buffer.from(parts.v1, 'hex'))) throw Error('signature');
// Atomically persist payload.id in your event store before processing.
// For a duplicate event return 2xx without applying the operation again.

Доставки и повторни опити

В „Заявки и доставки“ виждате устойчивите записи, HTTP резултата, опитите и следващото време. Чакащо събитие още не е доставено. Пет опита използват паузи 60, 300, 1800 и 7200 секунди. След 10 последователни неуспеха адресът се спира. Поправете адреса, включете го отново и повторете изчерпана доставка (до 3 ръчни серии с потвърдена самоличност). ID на събитието и доставката се запазват; получателят трябва да обработва дубликати безопасно.

Провери доставките

Създайте предаване

POST /api/v1/handovers създава само чернова. Задължителни са title и package_type. Отговор 201: {data:{id,status:"draft",created_at,sent_at:null,expires_at}}. Качете files[] чрез multipart POST /handovers/{id}/files с files:write. Добавете получател чрез POST /handovers/{id}/recipients с recipients:write и name/email или network_organization_id. Файловете преминават през съществуващото сканиране. Завършете прегледа и изпращането в MediSend; няма API за заобикаляне на този контрол. Не качвайте към изпратено, изтекло или отменено предаване.

За да създадете предаване, клиниката трябва да има право на изпращане по текущия си план или изрично започнат пробен период. API ключът не започва автоматично абонамент или пробен период.

Провери плана на клиниката
curl
curl --fail-with-body "$API_BASE_URL/api/v1/handovers" \
  -H "Authorization: Bearer $MEDISEND_API_TOKEN" \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --data '{"title":"Partner handover","package_type":"general_medical_file_transfer"}'

Статус и история

GET /api/v1/upload-requests/{id} връща {data:{id,reference,status,created_at,expires_at}}. GET /api/v1/handovers/{id} връща статус и files с id, sha256_hash, size_bytes, scan_status и uploaded_at само за файловете, качени през API. Не връща имена, медицински метаданни, клинични прикачени файлове, отговори на получатели или достъп. Списъците /handovers и /upload-requests приемат page и per_page (1–100, по подразбиране 25), връщат data и meta:{current_page,last_page,per_page,total}. Включват само операции, създадени от интеграции в организацията. Чужд или несвързан запис връща 404.

curl
export REQUEST_ID="<data.id from the create response>"
curl --fail-with-body "$API_BASE_URL/api/v1/upload-requests/$REQUEST_ID" \
  -H "Authorization: Bearer $MEDISEND_API_TOKEN" -H 'Accept: application/json'

Удостоверяване

Всички /api/v1 маршрути изискват Authorization: Bearer и Accept: application/json. Ключовете принадлежат на една организация, съхраняват се само като SHA-256 хеш и имат избрани права. Срокът и последното използване са видими в Настройки; последното използване се обновява най-много веднъж на 5 минути. Оттеглянето спира следващата заявка с 401.

Повторение на заявки

При създаване на чернова, получател и заявка изпращайте Idempotency-Key с 8–128 URL-безопасни знака. Ключът е за една операция и API ключ и се пази 24 часа. Повторение със същите данни връща оригиналния отговор и Idempotent-Replayed: true; различни данни връщат 409. Достъпът в оригиналния отговор се пази криптиран за безопасно повторение. Качването на файлове не е идемпотентно: след неясен резултат прочетете GET /api/v1/handovers/{id} с handovers:read и сравнете files по SHA-256 и размер преди повторение. Статусът на сканиране е отделен от приемането на файла.

Грешки и ограничения

Всички отговори имат X-Request-ID. Грешките имат error:{code,message,request_id,fields}. Кодове: 401 invalid_credential, 403 scope_denied, 404 not_found, 409 conflict, 422 invalid_request, 429 rate_limited; останалите request_failed. Запазете референцията за поддръжката. Ограничението е 120 заявки/минута за API ключ. Спазвайте Retry-After при 429 и ограничете паралелните заявки. Диагностиката пази маршрут, метод, резултат, време и име на ключа; не пази тела или заглавки.

Сигурност

Използвайте минимални права и отделен ключ за всяка система. Не включвайте тайни в URL, браузърния код, логове или запитвания до поддръжката. API не предлага глобално търсене на пациенти или медицински пакети. Достъпът остава специфичен за пакет, проверим и отменяем. Webhook не предоставя достъп до файлове. Частни адреси и пренасочвания са забранени. При компрометиране създайте нов ключ, сменете го в системата и оттеглете стария.

API маршрути

Наличните маршрути от публикувания OpenAPI договор.

GET/api/v1/handovers

Статуси на предавания, създадени през API

Необходимо право: handovers:read

HTTP отговори: 200 · 401 · 403 · 404 · 422 · 429

Пример и поведение →
POST/api/v1/handovers

Създаване на чернова за предаване

Необходимо право: handovers:write

HTTP отговори: 201 · 401 · 403 · 409 · 422 · 429

Пример и поведение →
GET/api/v1/handovers/{packageId}

Преглед на статус на предаване

Необходимо право: handovers:read

HTTP отговори: 200 · 401 · 403 · 404 · 429

Пример и поведение →
POST/api/v1/handovers/{packageId}/files

Качване на файлове в чернова

Необходимо право: files:write

HTTP отговори: 201 · 401 · 403 · 404 · 409 · 422 · 429 · 503

Пример и поведение →
POST/api/v1/handovers/{packageId}/recipients

Добавяне на получател от мрежата или ръчно

Необходимо право: recipients:write

HTTP отговори: 201 · 401 · 403 · 404 · 409 · 422 · 429

Пример и поведение →
POST/api/v1/upload-requests

Създаване на заявка за файлове

Необходимо право: upload_requests:write

HTTP отговори: 201 · 401 · 403 · 409 · 422 · 429

Пример и поведение →
GET/api/v1/upload-requests

Статуси на заявки, създадени през API

Необходимо право: upload_requests:read

HTTP отговори: 200 · 401 · 403 · 404 · 422 · 429

Пример и поведение →
GET/api/v1/capabilities

Възможности за интеграция на организацията

Необходимо право: integration:read

HTTP отговори: 200 · 401 · 403 · 404 · 422 · 429

Пример и поведение →
GET/api/v1/upload-requests/{requestId}

Преглед на статус на заявка, създадена през API

Необходимо право: upload_requests:read

HTTP отговори: 200 · 401 · 403 · 404 · 422 · 429

Пример и поведение →

Права

integration:read
Възможности на организацията
handovers:read
Статус и история на API предавания
handovers:write
Създаване на чернови
files:write
Качване в активни чернови
recipients:write
Добавяне на получател
upload_requests:write
Създаване на заявка
upload_requests:read
Статус и история на API заявки

Събития

request.created
Създадена заявка
package.created
Създадена чернова
package.sent
Изпратено предаване
package.opened
Отворено предаване
package.acknowledged
Потвърдено получаване
file.uploaded
Качен файл (не доказва чисто сканиране)
upload_request.submitted
Получени поискани файлове
verification.updated
Променена проверка
webhook.test
Изрично тестово събитие

Решаване на проблеми

Запазете X-Request-ID от отговора. Изберете проблема, за да видите безопасната следваща стъпка.

401Ключът не е приет

Проверете правилната среда, срока и оттеглянето на ключа. Създайте нов ключ, ако старият е изтекъл или оттеглен; заменете го в хранилището на системата.

Към подробните стъпки →
403Липсва нужно право

Проверете нужното право за маршрута в справочника. Създайте заместващ ключ само с необходимите права и оттеглете стария след успешна проверка.

Към подробните стъпки →
409Повторението не съвпада

Сравнете операцията и изпратените данни с първия опит. Повторете същите данни със същия Idempotency-Key. Нов ключ е за нова операция; не го сменяйте, за да заобиколите неизвестен резултат.

Към подробните стъпки →
422Действието не е прието

Коригирайте error.fields. Ако няма посочени полета, проверете плана и ограниченията за операцията в MediSend. Повтарянето без промяна няма да отстрани причината.

Към подробните стъпки →
429Достигнат е лимитът

Изчакайте Retry-After и намалете паралелните заявки. Запазете същия ключ при повторение на същата операция.

Към подробните стъпки →
TimeoutРезултатът е неизвестен

При създаване повторете същата операция със същия ключ в допустимия срок. При качване първо сравнете записаните SHA-256 и размер чрез GET на предаването; качването няма идемпотентност.

Към подробните стъпки →
WebhookИзвестието не е получено

Вижте конкретната доставка, HTTP резултата и следващия опит. Поправете приемащата система преди ръчно повторение. Потвърденото HTTP приемане не доказва приключена обработка във Вашата система.

Към подробните стъпки →
Отвори диагностиката