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 без запис на пакет.
// 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
// 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 ключ# 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 --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 --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}'{"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.
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 --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.
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 приемане не доказва приключена обработка във Вашата система.
Към подробните стъпки →