Recipients
A recipient is a person your organization may message on Telegram, with the basis you have for doing so. Oqim sends only to recipients with a recorded basis, and never to anyone who opted out.
Fields#
A Telegram ID and a username are each unique within an organization. Importing someone who already exists updates them instead of creating a duplicate.
Consent states#
Record the basis truthfully: it's what you would show if a recipient, Telegram or a regulator asked why someone received a message. See Compliance.
Reaching people by ID or username#
Telegram only lets an account message a user by numeric ID when the account already knows them: they're in its contacts or have written to it. Otherwise the send ends as unreachable with PEER_UNRESOLVABLE. A public username always works, so include usernames when you have them. If a username now belongs to a different Telegram user than the ID on file, Oqim doesn't send (USERNAME_REASSIGNED).
Importing#
Recipients → Import takes a CSV or JSON file of up to 10 MB and 100,000 rows. The same endpoint runs a dry run and the real import, so you see exactly what will happen before anything is saved.
CSV format
- A header row, UTF-8. Commas, semicolons and tabs are all recognised as the separator.
telegram_idorusernameon every row; both is fine. Usernames may include the@or be at.me/link. They must be 4 to 32 letters, digits or underscores and start with a letter.- Optional:
first_name,last_name,language,consent_status,consent_timestamp(a date, or a date and time) andtagsseparated by semicolons. Headers are case-insensitive, and common variants work:tg_id,name,surname,consent,lang. - Any other column becomes an attribute you can use as a variable. Its name is lowercased, with other characters turned into underscores: a column
Event Datebecomes{{event_date}}. - Rows without a consent status get the import's default consent, which is
UNKNOWNunless you choose another. Consent values are case-insensitive.
Here event_date and location become attributes, and Nodira, with no consent status, gets the default. If a spreadsheet shows long IDs as 1.23E+08, format the column as text before exporting; Oqim rejects IDs in scientific notation rather than guessing.
Dry run, then commit
Post the file as multipart/form-data with dry_run=true. Oqim checks every row and returns a report without writing anything:
Send the same file with dry_run=false to import it. The response has the same shape with created, updated and an import_id; past imports are listed at GET /recipient-imports. A report lists at most 500 issues.
Integrations can skip the file: send application/json with the rows in records, using the same keys as the CSV columns (tags may be an array and attributes an object), plus the fields above. source then defaults to API.
Validation issues
Each issue names the row (the header counts as row 1, as in a spreadsheet), the field and value when relevant, a kind and a readable message:
Lists, tags and bulk changes#
Lists are named groups you add people to; tags are labels on the recipient. A campaign's audience is the union of its lists, everyone carrying any of its tags, and individually picked recipients. Opted-out and suppressed people are left out when the campaign launches and checked again right before each send, whatever the audience says. Deleting a list keeps its recipients.
POST /recipients/bulk applies one action to up to 5,000 recipients: tag, untag, add_to_list, remove_from_list, set_consent, opt_out or delete. set_consent can't opt anyone out; that's what opt_out is for.
Keep recipients in sync with your customer database#
To mirror contacts from a CRM or your own database, send them to POST /recipients/import as JSON, on a schedule or whenever they change. It's the endpoint that updates as well as creates: each record is matched to an existing recipient by Telegram ID, then by username, and updated; a record that matches no one creates a recipient. POST /recipients only creates, and answers 409 recipient_exists for anyone you already have, so keep it for one-off additions.
The response is the import report (201 Created) with how many recipients were created and updated. Send "dry_run": true first to get the same report without saving anything. When a record matches an existing recipient:
- An import never deletes anyone. For people who left your database, use
POST /recipients/bulkwithdeleteorremove_from_listand their Oqim IDs;GET /recipients?q=with a Telegram ID or username finds them. Simpler still, import each sync into a new list withnew_list_nameand aim campaigns at the latest one: whoever is missing from your export drops out of the audience. Deleting an old list keeps its recipients. - Opt-outs go both ways. Subscribe a webhook to
recipient.opted_out, then readGET /recipients/{id}for the Telegram ID, username and attributes such ascrm_idto update your side. Opt-outs your side records come in asOPTED_OUTrecords, as above. - One request takes up to 100,000 records and 10 MB, and an organization's imports run one at a time. An import that would take you past your plan's recipient limit is refused (
403 plan_limit_reached) before anything is saved.
Opt-out#
Add {{opt_out_url}} to a message and every recipient gets a personal link such as https://app.example.com/u/b3JnXzAxajlyMm02azRu.q8Zr0mX2pLk5Vd7nWc3TyA. The token is signed, so it can't be guessed or altered, and it keeps working for as long as the recipient exists. The page asks the person to confirm, in Uzbek, Russian or English depending on their browser, with a switcher for the others.
An opt-out is permanent and applies to the whole organization. The moment it's confirmed:
- the recipient's consent becomes
OPTED_OUTand they're added to the suppression list; - sends to them that haven't gone out yet, in any campaign, are skipped;
recipient.opted_outis emitted, so your own systems can record it too.
When someone asks to stop in another way, by email or in a reply, record it with Opt out on the recipient, or POST /recipients/{id}/opt-out. If they aren't a recipient yet, add them to the suppression list.
Suppression list#
The suppression list holds Telegram IDs and usernames your organization must never message: everyone who opted out, plus anyone you add. It outlives deleted recipients, and future imports of the same person arrive opted out. Each entry records its source: OPT_OUT_LINK, MANUAL, API, IMPORT or ADMIN.
Adding an entry opts out any existing recipient with that ID or username at once; the response says how many. You can remove entries you added by mistake, but removing one never restores consent: recipients who opted out stay opted out.