API reference

Push contacts in from your own systems, fire transactional mail, and pull campaign results. Base URL https://sendora.caresoft.co.in/api/v1. JSON in, JSON out.

Authentication

A client admin creates keys under Settings → API. The key is shown once; only a hash is stored, so it cannot be recovered later.

curl https://sendora.caresoft.co.in/api/v1/ping \
  -H "Authorization: Bearer snd_live_..."

Every response carries ok: true or ok: false with an error object. Rate limits are per key and reported in X-RateLimit-Limit and X-RateLimit-Remaining.

Endpoints

MethodPathScopeWhat it does
GET /ping — Confirms the key works and names the account.
GET /datasets contacts Every contact database, with counts.
POST /datasets contacts Create a database. Body: name, description, tag.
GET /contacts?dataset_id=1&page=1 contacts Paged contacts with their status and custom fields.
POST /contacts contacts Upsert up to 1000 contacts. Matched on email inside the database.
POST /send send One transactional email, right now, through your SES account.
GET /campaigns reports The last 100 campaigns with headline numbers.
GET /campaigns/{id} reports Full report for one campaign.
GET /campaigns/{id}/recipients?status=bounced reports Per-contact results, paged.
GET /suppressions contacts Everything blocked for this account, and platform-wide blocks.
POST /suppressions contacts Block one or many addresses. Body: addresses[], channel.
DELETE /suppressions contacts Release an address. Body: address.

Adding contacts

Anything you put in fields becomes a merge variable, exactly like a column in an uploaded spreadsheet. Contacts are matched on the email address within that database, so re-sending the same payload updates rather than duplicates.

curl https://sendora.caresoft.co.in/api/v1/contacts \
  -H "Authorization: Bearer snd_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "dataset_id": 1,
    "check_mx": true,
    "contacts": [
      {"email": "[email protected]",
       "first_name": "Rao", "company": "Sunrise Multispeciality",
       "city": "Thane",
       "fields": {"beds": "120", "speciality": "Cardiology"}}
    ]
  }'
{"ok":true,"added":1,"updated":0,"rejected":0,"errors":[]}
An address already on your suppression list is still stored, but marked so no campaign will use it. That is deliberate: you keep the record, you just cannot mail it.

Transactional send

For one-off mail your own application triggers — a booking confirmation, a report link. It goes through the same SES account, MIME builder and suppression checks as campaigns.

curl https://sendora.caresoft.co.in/api/v1/send \
  -H "Authorization: Bearer snd_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "[email protected]",
    "template_id": 4,
    "variables": {"first_name": "Rao", "appointment_date": "12 March"}
  }'

Or pass subject and html directly instead of a template id. Merge variables work in both.

If the address is suppressed, the call returns ok: true with skipped: true and a reason, not an error. Refusing to send is the correct outcome, and your integration should not retry it.

Reading results

curl "https://sendora.caresoft.co.in/api/v1/campaigns/12/recipients?status=bounced&per_page=500" \
  -H "Authorization: Bearer snd_live_..."

Useful statuses: sent, delivered, opened, clicked, bounced, complained, unsubscribed, failed, skipped_suppressed, skipped_invalid.

Errors

StatusCodeMeaning
400bad_jsonThe body was not valid JSON.
401missing_key / invalid_keyNo bearer token, or the key was revoked.
403scope_missingThe key does not carry the scope this endpoint needs.
404dataset_not_foundThe id does not belong to this account. Ids are never shared across accounts.
409duplicate_nameA database with that name already exists.
412no_senderNo verified sending identity is set up yet.
422bad_recipientSomething in the body was missing or malformed. The message says which.
429rate_limitedOver the per-minute limit on that key. Check the X-RateLimit headers.
502provider_errorAmazon refused the message. The message text is passed through unchanged.