Profiles and consents
Consents — what to show the user
Only people who turned on participation themselves are added to the network. Add toggles with short explanations to your profile settings. Below are texts you can use as is; just change the app name.
“Show me in the Pereval network” — required
The toggle is off by default. Next to it, put a link to pereval.dating, which explains what the network is.
Show me to people from other apps Your profile (name, age, city, photos, bio) will be visible to users of other dating apps connected to the Pereval network. You chat with them right here in [Your app]. Your phone number and email are not sent to the network. The network receives coordinates from your app and stores rounded coordinates; other users do not receive coordinates. You can turn participation off at any time — new people from other apps won’t see you, and your chats stay. “Delete my data from the network” removes your profile and closes matches, but does not erase the other person’s chat history: it remains without your name and photos. Separate copies of messages may be retained to investigate reports.
“Open to chatting in other languages” — optional
Meet people who speak other languages Messages and profile bios can be translated automatically when the translation service is available. The original is always available; if translation fails, the original is shown.
“Worldwide” — optional
For people who want to meet someone from another city or country. Those who turn it on get the Worldwide feed (scope=world, see Feed) and appear in it themselves — but only to others who turned it on too. Offer it together with other languages: people from abroad usually speak a different one.
Meet people from other cities and countries Your feed gets a Worldwide tab — people from anywhere, no distance limit. Only people who turned it on too are there, and only they see you.
“Show distance to me” — optional
Off by default. Does not change the profile city. Other people’s cards get distance_km only if this person turned it on.
Show distance to me Others will see how many kilometres away you are. Your exact address is never shown.
Searching another city is a feed lat/lon parameter, not a city change on the profile (the city can still change only once a day).
What to send to the network
"consent": {
"network": true,
"cross_language": true,
"world": false,
"given_at": "2026-10-24T12:00:00Z",
"text_version": "2026-10-24"
}
given_at— when the person actually turned on the toggle (store it on your side).text_version— the version of the text they saw. Using the date the text changed works well. If the text changes substantively, ask for consent again and update the version.- Both values must be real. Uploading a user base without consent gets the platform disconnected — see the network rules.
Intents
intents says what the person is looking for. Two people meet in the feed only if they share an intent.
| Value | Intent |
|---|---|
serious |
A serious relationship |
casual |
Something casual |
friendship |
Friendship, chatting |
arrangement |
A relationship with a well-off partner |
If omitted, it defaults to ["serious", "casual", "friendship"].
Relationships with a well-off partner
A separate, strictly opt-in category.
- It’s enabled for your app when you join the network. Without that, profiles with
arrangementare rejected (422). arrangement_roleis required:provider— ready to support a partner;seeker— looking for a well-off partner.- If two people share only
arrangement, they see each other only with opposite roles. - The other person’s role is shown only to viewers who are in this category themselves.
- Offering sex for money, price lists and “paid meetings” are forbidden in every category. A
commercialreport removes the member from the network at once.
User-facing text:
Looking for a relationship with a well-off partner / Ready to support a partner Only people who chose the opposite option will see you. This is about relationships, not services: offering paid meetings is forbidden and leads to a ban.
When to send a profile
Call PUT /v1/members/{external_id}:
- when a person turns on participation in the network;
- on any change to the profile, photos, preferences, city, languages or consents;
- when a person has passed your photo verification — with
verificationor the network’s check.
The request is idempotent: sending the same profile again breaks nothing. Save the network_id from the response — it lets you recognize your user in other people’s feeds and avoid showing them to themselves.
The network translates the bio in the background, after the profile is saved. Until the translation is ready, other people’s feeds show the original.
First upload
When you connect, you may already have people who turned participation on. Send them in batches: PUT /v1/members, up to 100 profiles per request.
PUT /v1/members
{
"items": [
{ "external_id": "user-123", "consent": { … }, "display_name": "Anna", … },
{ "external_id": "user-124", "consent": { … }, "display_name": "Olga", … }
]
}
Each profile is validated on its own: one bad profile doesn’t block the rest. The response has a result for each one, in the same order:
{
"items": [
{ "external_id": "user-123", "network_id": "6f1c2b4e-…" },
{ "external_id": "user-124", "error": { "code": "invalid", "message": "birth_year: members must be 18+" } }
]
}
Only people who turned participation on themselves: never upload your whole user base.
Profile requirements
| Field | Rule |
|---|---|
display_name |
1–50 characters. First name only, no last name or contacts |
birth_year |
Age 18+ |
gender |
male or female; the network matches people of the opposite gender |
age_min, age_max |
18 ≤ age_min ≤ age_max |
languages |
ISO 639-1 codes (two letters, otherwise 422), the first one is primary. The person speaks these languages, so the network never translates them for this person; everything else is translated into the primary one. Translation languages — GET /v1/languages, 38 languages |
city |
Name and country, or coordinates. The network rounds coordinates to ~1 km, but it’s better to send the city center right away. The city can change once a day |
bio |
Up to 1000 characters |
photos |
1–9 public https:// URLs. No watermarks with contacts. The network may cache photos |
videos |
optional: up to 3 clips next to the photos, not instead of them. Each has url (MP4, up to 15 seconds) and poster (a still). Omit or [] to clear |
verification |
how and when you checked that the person matches the photos — method, provider, verified_at. Details below; or by the network’s check: provider: "pereval" and check_id (below) |
photo_verified |
Before 0.15 — a “verified” flag. Still accepted as a claim with an unspecified method at the time it arrives; send verification instead |
height_cm |
optional: height in centimeters, 120–230. Don’t know it — leave it out |
show_distance |
off by default: other people’s cards may show distance_km |
A validation error returns 422 with the field in message, for example "birth_year: members must be 18+".
Photo verification
The “photo verified” badge in other apps’ feeds and the verified_only filter use the photo_verified mark, which the network computes. You send a claim — how and when you checked the person:
"verification": { "method": "selfie", "provider": "own", "verified_at": "2026-10-20T14:03:00Z" }
| Field | Value |
|---|---|
method |
selfie — a photo selfie (e.g. with a gesture); video — a video selfie; liveness — an automated liveness check; document — an ID with a face match; in_person — met in person; other |
provider |
Who did the check: a vendor (e.g. Sumsub) or own. Up to 60 characters |
verified_at |
When the check passed. Not in the future |
The mark counts if:
- The network has approved your verification method. Describe how you verify photos when you connect; the network reviews the method once. Until it is approved, your users’ marks don’t count in the network. Platforms connected before 0.15 were approved on upgrade.
- None of the current photos is newer than the check. The photos a person joins the network with are covered by a check made before you connected — you don’t need to re-verify your users. A photo added after the check switches the mark off until you send a newer
verified_at. The network recognises the same picture under a new URL by its content.
Whether the mark counts and why is in the PUT /v1/members response:
{ "network_id": "…", "photo_verified": false, "verification": "photos_changed" }
verification |
Meaning |
|---|---|
accepted |
The mark counts |
none |
No claim |
no_photos |
No photos |
partner_not_approved |
The network hasn’t approved your verification method yet. Never happens for claims by the network’s check |
photos_changed |
A photo is newer than the check: verify the person again and send a newer verified_at |
The network re-checks a sample of verified people and those with fake signals: “fake” reports, the same photo on another person, generated images. If a platform’s marks fail the re-checks, the network stops counting them for all of its users.
Photo check by the network
No verification of your own? Use the network’s photo check. It’s free. The person takes it on a network page: agrees to the comparison, shows the requested gesture to the camera and takes a picture — with the camera only, not from the gallery. The network compares the picture with each profile photo and tells you the result for each photo. What to do with the result is up to you.
You don’t need to store pictures or facial data — you never get them. The person’s identity is not established from the picture and no facial features are stored. The picture is never shown to anyone: if the check passes, it is deleted right away; if it fails, it is kept for 24 hours so the person can dispute the result; if disputed, until the network moderator decides. The consent on the page names both you and the network: the network compares the photos on your behalf.
1. Create a session — from your server, with your API key:
POST /v1/photo-checks
{
"ref": "u_123",
"lang": "en",
"return_url": "https://your.app/verified",
"photos": [
{ "id": "ph1", "url": "https://cdn.your.app/ph1.jpg" },
{ "id": "ph2", "url": "https://cdn.your.app/ph2.jpg" }
]
}
| Field | Meaning |
|---|---|
ref |
How you will recognise the person in the result, usually their external_id. 1–200 characters |
photos |
1–9 profile photos — public https URLs. id is your photo identifier, the position by default. The network downloads the photos right away |
lang |
Page language: ru or en |
return_url |
Optional: an https address for the “Back” button at the end of the check |
Reply 201 — the session, with the check page url and expires_at. At most 10 sessions per person a day (429 photo_check_limit); the comparison service is unavailable — 503 photo_check_off, retry later.
2. Open the url for the person — in the browser, a new tab or your app’s in-app window (a webview with camera access). The link is valid for 30 minutes and allows up to 5 attempts. The page can’t be embedded in an iframe on your site. Besides the event, the page passes the result to the window that opened it: postMessage({ type: "pereval.photo_check", status }).
3. The result comes as a photo_check.updated event — after each attempt, when the person disputes the result and when the moderator decides; go by the latest one. The same is in GET /v1/photo-checks/{id}:
{
"id": "…", "ref": "u_123", "status": "partial", "attempts": 1,
"checked_at": "2026-10-30T09:14:00Z", "dispute_until": "2026-10-31T09:14:00Z",
"photos": [
{ "id": "ph1", "url": "https://cdn.your.app/ph1.jpg", "verdict": "match" },
{ "id": "ph2", "url": "https://cdn.your.app/ph2.jpg", "verdict": "no_match" }
]
}
status |
Meaning |
|---|---|
created |
The person hasn’t taken the check yet |
passed |
It’s them on every photo with a face |
partial |
Some photos don’t look like the picture |
failed |
Didn’t pass; why — in reason: no_match (no photo looks like them), no_face, many_faces, no_gesture (gesture not recognised), virtual_camera |
disputed |
The person disputed the result; a network moderator decides — usually within 24 hours |
resolved |
The moderator decided: the per-photo result is theirs, note is an explanation for the person |
expired |
The link expired and the person never took the check |
Per-photo verdict: match — the same person; no_match — doesn’t look like them; unsure — couldn’t compare reliably; no_face — no face, not counted.
4. The mark in the network. Once the check has passed (passed or resolved), send a claim that refers to it:
"verification": { "provider": "pereval", "check_id": "…" }
The network verifies that the check is yours, has passed and that all current profile photos were in it with a match or no_face verdict, at least one match. A photo is recognised by its URL or its content. Then the mark counts even if the network hasn’t approved your own verification method yet, and verified_at is the time of the check. Otherwise — 422 with the reason. A new photo after the check switches the mark off as before: take the person through the check again.
Changing the city
The city can change once a day. If you send a new city, or a location more than 50 km away, sooner than 24 hours after the previous change, the network returns 409 location_change_limited. Moving around within the city is not limited.
The profile is not updated in that case. Tell the person they can change the city in a day, and send the other changes with the previous city.
Photos
The network shows photos from your URLs. Requirements:
- URLs are accessible without authentication and don’t expire (or update the profile when the URLs change);
- EXIF metadata is stripped from the files — it can contain location;
- the first photo is the main one and shows the face clearly.
Video
Production video is disabled until frame moderation exists. Check GET /v1/capabilities: when videos: false, nonempty videos is rejected. The following describes the protocol format, not a promise of availability in the live environment.
A short clip on the card, next to the photos. Photos stay required: face checks use them, and an app that does not play video still has a card.
"videos": [
{
"url": "https://cdn.example/u/1.mp4",
"poster": "https://cdn.example/u/1.jpg"
}
]
- Up to three clips. A repeated
urlis422. urlis a public MP4, at most 15 seconds and 20 MB. The file stays on your host; the feed carries the link.posteris a still. Apps show it before playback, and apps that cannot play video show it instead.- The protocol has no mute flag. A clip starts silent. One switch in the viewing app turns sound on for every clip, not a flag on each one.
- The poster is moderated like a photo. The network downloads the file and reads its duration; it does not decode frames. Not an MP4 —
video_invalid, longer than 15 seconds —video_too_long, could not be downloaded —video_unavailable. The clip URL arrives invideoonmember.rejected.
Moderation
A new profile, or one with changed photos, clips, name or bio, is checked by the network before it appears in feeds — usually within seconds. Not allowed:
- explicit photos;
- insults, threats, obscenity, offers of paid meetings;
- contact details — phone, link, messenger handle: people chat inside the apps;
- photos that cannot be downloaded (after several attempts).
If a profile does not pass, you get the member.rejected event with the reason: photo_nudity, offensive_text, contact_info, photo_unavailable, video_invalid, video_too_long or video_unavailable. A photo or poster URL is in photo; a clip URL is in video. The profile is hidden and cannot like until you send a corrected one; resending the same profile does not trigger a new check. Photos and text that already passed are not checked again.
It’s better to check profiles on your side first: it’s easier for a person to fix the profile right away than after the network rejects it.
Activity
The feed favors people who were online recently and hides those inactive for more than 30 days. Report activity with POST /v1/members/{id}/active — no more than once every few minutes per user. A profile PUT also updates activity.
With many users, a batch is easier — every few minutes, for everyone who opened the app in that time:
POST /v1/activity
{ "external_ids": ["user-123", "user-124", "user-125"] }
Up to 1000 ids per request. The response is {"updated": 3}: how many members were found and updated. Unknown ids are skipped.
Turning off: hide, don’t delete
When a person turns participation off, send the same profile with consent.network: false. The network hides them instead of deleting:
- the record and
network_idstay — matches, conversations, dislikes and blocks are kept; - they are in nobody’s feed and get no feed themselves (
409 member_hidden); - they can only like back someone who already liked them — that is a match right away; no super likes (
409 member_hidden); - conversations in existing matches go on both ways;
- photos are optional while hidden — they may have deleted them all.
Turned it on again — send consent.network: true: the person is visible again, with their whole history. Turning it on and off any number of times never creates new profiles.
When a hidden person replies to someone from the network (writes in a conversation or likes back), offer to come back: “Show you in the network again?” with “Reply and rejoin the network” and “Just reply”. Rejoin only on that tap: it is consent to show the profile again.
Deferred account deletion
If your app gives 30 days to restore an account, tell the network that the person is “deleting”:
POST /v1/members/{external_id}/deactivate
The profile disappears from feeds and “liked you”, matches are frozen: the other people’s platforms get match.frozen — show “Account deleted”, messaging is blocked (409 match_frozen). Nothing is erased. They came back — POST /v1/members/{external_id}/restore: everything is as it was, the others get match.unfrozen. Didn’t come back within 30 days — delete them (DELETE below); if you don’t, the network erases them itself: personal data must be destroyed no later than 30 days after consent is withdrawn.
Deletion
Call DELETE /v1/members/{external_id}?reason=account_deleted when a person deletes their account with you; ?reason=left_network (the default) when they tap “Delete my data from the network”. The network:
- closes their matches: the other side receives
match.closedwith thatreasonandpeer_deleted: true; - the conversations stay with the other people, like in Telegram — show them without the name and photo: “Account deleted” / “Left the network”;
- erases the profile (name, photos, bio, city), likes, dislikes and blocks.
If the person turns participation on again, it will be a new profile without the old matches.