Raktify Developer API

Public REST webhook contract for blood-bank software vendors to sync donor and donation records into Raktify, India's district-wide blood-response platform.

One-line pitch for your engineering team: your software already captures every donor registration and every collection event; a single HMAC-signed HTTPS POST after each event mirrors it into Raktify so donors can be reached for compatible requests in their district. No polling, no scraping, no dual-entry, no BSP.

Everything you need, in one place

Overview

Raktify is a not-for-profit blood-response platform built by Choudhari EduHealth India Foundation. It runs the matching pipeline between hospitals with urgent blood requests, blood banks holding tested inventory, and voluntary donors in the surrounding district.

Blood banks already run software — Safetrans (Strides), e-RaktKosh, BLIS, RAKT, or homegrown Excel/Access systems. Asking a technician to also type each donor and donation into Raktify is friction that will kill adoption. Instead we publish a single webhook contract: your software pushes an event, our platform mirrors it. Direction is vendor → Raktify (push) — you don't need to open any inbound ports or expose credentials.

Who this is for

Quickstart

  1. Request a partner key for each hospital installation you integrate.
  2. Every donor registration your operator completes: POST to /webhooks/v1/donor-registration.
  3. Every donation event (draw + TTI screening result): POST to /webhooks/v1/donation.
  4. Sign each request body with your partner key's HMAC-SHA256 secret; carry X-Raktify-Partner-Key and X-Raktify-Signature headers.
  5. Include a vendor_event_id in every payload. Retries with the same ID are safe (idempotent).

Authentication

HMAC-SHA256 request signing

Every request MUST carry two headers:

HeaderValue
X-Raktify-Partner-KeyYour pk_... identifier — one per hospital installation.
X-Raktify-Signaturesha256=<hex> — HMAC-SHA256 over the raw request body, keyed by your partner secret.
Content-Typeapplication/json

Signing example (Node)

const crypto = require('crypto');
const body = JSON.stringify(payload);
const signature = crypto
  .createHmac('sha256', PARTNER_SECRET)
  .update(body)
  .digest('hex');

fetch('https://raktify-api.azurewebsites.net/webhooks/v1/donor-registration', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Raktify-Partner-Key': 'pk_your_key_here',
    'X-Raktify-Signature': 'sha256=' + signature,
  },
  body,
});

Signing example (Python)

import hmac, hashlib, json, requests

body = json.dumps(payload).encode('utf-8')
sig = hmac.new(PARTNER_SECRET.encode('utf-8'), body, hashlib.sha256).hexdigest()

requests.post(
    'https://raktify-api.azurewebsites.net/webhooks/v1/donor-registration',
    data=body,
    headers={
        'Content-Type': 'application/json',
        'X-Raktify-Partner-Key': 'pk_your_key_here',
        'X-Raktify-Signature': 'sha256=' + sig,
    },
)
Sign the raw body bytes, not the parsed object. If your HTTP client serializes JSON differently from what you passed to the HMAC (key ordering, whitespace, escaping), the signature will not verify. Serialize once, sign that byte string, send that byte string.

Getting a partner key

Partner keys are for blood-bank software vendors only. This is a one-way push API for software that runs at a hospital or blood bank, and keys are not issued to individual developers or students. It has no endpoints to search donors, check availability or contact a donor; we do not expose donor data to third-party applications.

Partner keys are issued per hospital installation. If your software serves five hospitals, you get five distinct keys — that way we can revoke a single hospital's key without affecting the others.

To request a key, contact contact@choudhari.ngo with:

We reply with the partner_key and the HMAC secret (revealed exactly once — save it into your credentials store immediately). Self-service issuance via an admin panel is on the roadmap.

Endpoints

Base URL: https://raktify-api.azurewebsites.net

All request and response bodies are JSON. Successful writes return 202 Accepted (the row is stored and the consent flow is enqueued asynchronously).

POST /webhooks/v1/donor-registration

POST /webhooks/v1/donor-registration Idempotent

Upsert a donor by mobile. A donor pushed for the first time is stored with consent_pending — we send them a WhatsApp with a magic link to accept on Raktify's own consent screen (DPDP §7). Matching and alerts don't reach unconsented donors.

Request body

FieldTypeRequired?Notes
vendor_event_idstringRequiredYour side's event ID; used for idempotency. Max 255 chars.
event_timeISO 8601 datetimeRequiredWhen the operator completed the registration in your software.
mobilestringRequiredIndian mobile, +91 prefix or 10-digit; we normalize.
full_namestringRequired2–200 chars. Encrypted at rest.
date_of_birthYYYY-MM-DDRequiredDonor must be 18–65 (DB constraint).
genderenumRequiredM / F / O
blood_groupstringOptionalA+, A-, B+, B-, AB+, AB-, O+, O-. Trusts your BB's attestation.
pincodestringOptionalIndian 6-digit.
village_idintegerOptionalLGD code — if you have it. Preferred over pincode.
address_linestringOptionalEncrypted at rest.
abha_idstring(17)OptionalABHA ID if captured.
aadhaar_last4string(4)OptionalNever full Aadhaar.
preferred_languageenumOptionalmr / hi / en. Default mr.
consent_captured_atISO 8601 datetimeOptionalTimestamp your software captured the donor's "share with Raktify" tick. Belt-and-braces — the primary consent is always captured on Raktify's own /consent screen.
Unknown fields are silently ignored. If your software collects fields we don't require (email, guardian details, occupation, past-donation notes, whatever) — send them through anyway. We drop what we don't use. This means adding new fields your side does not require us to release a new API version.

Response — 202 Accepted

{
  "raktify_donor_id": "6fda231f-9e7e-4a1d-8761-87e15f83d1ee",
  "action": "created",             // "created" | "updated_pending" | "updated_consented"
  "is_new": true,
  "consent_status": "pending"      // "pending" | "active"
}

Error responses

CodeBodyMeaning
401{"error":"signature_mismatch"}HMAC did not verify. Check secret + body-bytes.
401{"error":"unknown_partner_key"}Partner key not recognized.
403{"error":"partner_key_revoked"}Key exists but is inactive.
422{"error":"invalid_input", "details":{...}}Zod validation error; details pinpoints the offending field.
422{"error":"invalid_mobile"}Not an Indian mobile.

POST /webhooks/v1/donation

POST /webhooks/v1/donation Idempotent

Record a donation event. The donor MUST already exist on Raktify — push /donor-registration first for a new donor and retry.

Request body

FieldTypeRequired?Notes
vendor_event_idstringRequiredYour side's event ID.
event_timeISO 8601 datetimeRequiredWhen the operator recorded the donation.
donor_mobilestringRequiredThe join key back to the donor (same as the earlier registration).
collection_dateYYYY-MM-DDRequiredMust not be in the future (DB constraint).
component_codestringRequiredMatches Raktify's blood_components.code — WB, PRBC, FFP, PLT, CRYO. Case-insensitive.
volume_mlintegerRequired1–1000.
isbt_barcodestringRequiredBag barcode. Must be unique across Raktify.
hb_gdlnumberOptionalDonor Hb at draw (g/dL).

Response — 202 Accepted

{
  "raktify_donor_id": "6fda231f-9e7e-4a1d-8761-87e15f83d1ee",
  "raktify_donation_id": "4ab05951-f222-4e14-9993-4ea38372b777",
  "action": "created"
}

Error responses

CodeBodyMeaning
409{"error":"donor_not_found"}Donor with that mobile hasn't been pushed yet. Retry after /donor-registration.
422{"error":"unknown_component"}component_code doesn't match blood_components.code.
422{"error":"invalid_input"}Field-level Zod error.
The trigger automatically creates a blood_inventory bag row in QA (quarantine) status when a verified donation lands. TTI screening results follow when your operator marks them in your software; that's a future extension of the API.

Reliability

Idempotency

Every request MUST include vendor_event_id. If your delivery layer retries after a network blip, retries with the same (partner_key, vendor_event_id) pair return the original result cached from the first attempt — the same raktify_donor_id, the same status. This lets you use at-least-once delivery semantics safely.

Idempotency records are retained for 30 days. Beyond that, a repeat of an old vendor_event_id is treated as a fresh request.

Rate limits

Currently 100 requests / IP / minute globally. Your partner key isn't rate-limited separately — the global limit is generous enough for any normal BB workflow (a busy blood bank processes ~50 donations per day). If you need higher throughput for a batch backfill, contact us to arrange a temporary uplift.

Responses on rate-limit: 429 with {"error":"rate_limit_global"} and a Retry-After header. Back off + retry.

Error reference

HTTPError codeAction
202(success)Row stored (or idempotent replay).
401missing_partner_key, missing_signature, signature_mismatch, unknown_partner_key, invalid_partner_key_formatFix auth headers.
403partner_key_revokedContact us for a new key.
409donor_not_foundPush /donor-registration first.
422invalid_input, invalid_mobile, unknown_componentFix payload; do NOT retry with same body.
429rate_limit_globalHonor Retry-After, then retry.
500internal_errorTransient — retry with backoff.

India's Digital Personal Data Protection Act (DPDP §7) requires explicit donor consent before we process their PII for platform purposes. Your push tells us the donor exists at a specific blood bank; consent is captured separately on Raktify's own screen.

  1. Your push lands. Donor row is created with consent_data_use=false and consent_pending_since=NOW().
  2. Raktify sends the donor a WhatsApp (approved Meta template donor_consent_invite) with a magic link to https://raktify.choudhari.ngo/consent/<token>.
  3. The donor sees Raktify's own consent screen — what data we hold, what we'll do with it, their DPDP rights — and taps Accept, Decline, or defers.
  4. Accept: consent_data_use flips to true; the donor becomes visible to matching for compatible requests in their district.
  5. Decline: we run the donor-erasure scrub (mobile tombstoned, name and address wiped) within seconds.
  6. No response in 14 days: the same scrub runs automatically. We don't retain PII we have no lawful basis to process.
What this means for you: Your operator does not need to collect Raktify-specific consent in your software. If your donor registration form has a "share with Raktify" checkbox, great — pass the timestamp in consent_captured_at as an audit signal. But Raktify's own consent step is the authoritative capture. Your push starts a conversation; the donor closes it on our side.

Interactive API browser

Browse the full OpenAPI 3.0 spec below — expand any endpoint to see the schema, examples, and error responses. Requests you fire from here hit the live production API; without a valid X-Raktify-Partner-Key + HMAC signature they'll return 401 signature_mismatch (that's the correct auth response). For real end-to-end testing use the Postman collection — it auto-signs with your partner secret.

Downloads & tooling

All artifacts for the vendor push webhook. Machine-readable and human-readable in one place.

The spec is versioned in the URL (/webhooks/v1/). Adding an optional field is not a version bump; changing semantics, removing, or renaming fields would be v2 and would run alongside v1.

Contact + support