TatvaOS · API keys · People API
People API Integration Guide
Let your own software add people to your TatvaOS organisation — a student information system, an HR package, anything that already knows who joins. One request per person. They set their own password; your software never touches it.
- What this is
- Getting a key
- Adding a person
- The fields
- Responses
- Code examples
- What a key cannot do
- Security checklist
- What is not in this version
What this is
One HTTPS request that creates a person in your organisation, exactly as an administrator would in the console. TatvaOS gives them an address on your verified domain, applies your plan's limits, and sends them an invitation to set their own password.
It is deliberately small: one endpoint, one authentication method, and a handful of fields.
At a glance
| Endpoint | POST https://core.tatvaos.com/api/v1/org/people |
| Authentication | Authorization: Bearer tvk_… — a People API key |
| Body | JSON: localPart, displayName, recoveryEmail or recoveryPhone, and optional others |
| Rate limit | 30 requests a minute, per calling address |
| Success | 200 with the person, who has been sent an invitation |
1. Getting a key
An organisation administrator creates keys in the TatvaOS admin: Organisation → API keys → People API. They name the key for the program that will use it ("Student information system"), tick what it may do, and hand it to you. It looks like this:
tvk_m4Kp9vRt2xQn7bLw3sHy6dFz8cJaG5eU
It is shown once. TatvaOS stores only a hash with a short visible prefix, so a list can tell two keys apart. Nobody can read the key back, including us. If it is lost, the administrator revokes it and creates another.
It does only what was ticked. Today there is one thing to tick — adding people — and a key with nothing ticked can do nothing at all. An unticked permission is refused by the server, not merely hidden in the console.
2. Adding a person
One POST, JSON body, key in the Authorization header.
POST /api/v1/org/people HTTP/1.1
Host: core.tatvaos.com
Authorization: Bearer tvk_m4Kp9vRt2xQn7bLw3sHy6dFz8cJaG5eU
Content-Type: application/json
{
"localPart": "asha.rao",
"displayName": "Asha Rao",
"recoveryEmail": "asha.rao@example.com"
}
That creates asha.rao@your-verified-domain and sends Asha an invitation at her personal address. She follows the link and chooses her own password. Nobody — not you, not your software, not the administrator — ever sets it or sees it.
Which domain
If your organisation has one verified domain, leave domainId out and TatvaOS uses it. If you have more than one, the request is refused with a message listing them and their ids, because guessing which domain a person belongs on is not a guess worth making.
3. The fields
| Field | Required | Meaning |
|---|---|---|
localPart | yes | The part before the @. "asha.rao" becomes asha.rao@your-domain. |
displayName | yes | Their name as colleagues will see it. |
recoveryEmail | one of | A personal address where the invitation is sent. Stored unverified until they follow the link, which proves it. |
recoveryPhone | one of | A mobile number with country code, if the invitation should go by SMS instead. |
domainId | no | Only needed when the organisation has more than one verified domain. |
departmentId | no | Puts them in a department, which sets their default products. |
role | no | Only employee is accepted. See what a key cannot do. |
products | no | Which products they get, for example ["mail"]. Defaults to the department's, then to mail. |
recoveryEmail or recoveryPhone is required. Without one there is
no way to deliver an invitation, and the request is refused rather than creating an account nobody can ever enter.
That is a deliberate refusal, not a validation oversight.
4. Responses
Every response is JSON. Read the status code first, then the body.
| Status | Meaning |
|---|---|
| 200 | The person exists and an invitation is on its way. The body carries their id and address. |
| 400 | Something in the request, and the message says what: no way to send an invitation, an invalid local part, an unverified domain, more than one verified domain with none named, or your plan's people limit reached. |
| 401 | The key is missing, malformed, unknown, or revoked. All four get the same answer on purpose — the response never reveals whether a key once existed. |
| 403 | The key is real but is not allowed to add people, or the request asked for a role a key may not grant. |
| 409 | That address already exists. Adding the same person twice is refused rather than silently ignored. |
| 429 | More than 30 requests in a minute from your address. Wait and continue; do not retry in a tight loop. |
Retries
Retry a 429 after a pause, and a network failure at once. Do not retry a 409 — the person is already there. Do not retry a 400, 401 or 403 unchanged; the same request gets the same answer.
5. Code examples
Each adds the same person. Replace the key and the addresses.
curl
curl -X POST https://core.tatvaos.com/api/v1/org/people \
-H "Authorization: Bearer $TATVAOS_PEOPLE_KEY" \
-H "Content-Type: application/json" \
-d '{"localPart":"asha.rao","displayName":"Asha Rao","recoveryEmail":"asha.rao@example.com"}'
Node.js (18+, built-in fetch)
const res = await fetch("https://core.tatvaos.com/api/v1/org/people", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TATVAOS_PEOPLE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
localPart: "asha.rao",
displayName: "Asha Rao",
recoveryEmail: "asha.rao@example.com",
}),
});
const body = await res.json();
if (res.status === 409) return; // already there, nothing to do
if (!res.ok) throw new Error(`${res.status}: ${body.error}`);
Python (requests)
import os, requests
r = requests.post(
"https://core.tatvaos.com/api/v1/org/people",
headers={"Authorization": f"Bearer {os.environ['TATVAOS_PEOPLE_KEY']}"},
json={
"localPart": "asha.rao",
"displayName": "Asha Rao",
"recoveryEmail": "asha.rao@example.com",
},
timeout=30,
)
if r.status_code == 409:
pass # already there
elif not r.ok:
raise RuntimeError(f"{r.status_code}: {r.json().get('error')}")
A joiners feed, end to end
The pattern most integrations follow. Your system holds the list of people who have joined. Nightly, or on each new record, it sends one request per person. Treat 409 as success — it means that person is already in TatvaOS, which is what you wanted — and you can re-send the whole list safely without creating duplicates.
Keep your own record of who you have sent, so a failure part-way through a batch can be resumed rather than restarted.
6. What a key cannot do
Stated plainly, because these are the questions a careful administrator asks before handing a credential to a program.
- It cannot make anyone an administrator. Only
employeeis accepted. Asking for an admin or owner role is refused, and so is inheriting one through a department whose default role is privileged. A key cannot escalate by any route. - It cannot set anyone's password. There is no password field. People set their own from the invitation.
- It cannot read your people. There is no list or lookup call yet. The key adds and nothing else.
- It cannot remove, suspend or edit anyone. Those stay in the console, where a person decides them.
- It cannot reach another organisation. The key names yours, and every read and write is confined to it by the database, not only by the code.
- It cannot send mail. That is the separate Mail API key.
7. Security checklist
| Do | Don't |
|---|---|
| Keep the key in a server-side secret store or environment variable. | Embed it in browser code, a mobile app, or a public repository. |
| Use one key per program, named for what it does. | Share one key between your HR system and a partner's. |
| Ask the administrator to revoke a key the moment you suspect exposure. It stops working on the next request. | Keep using a key that has been in a chat, a ticket, or a log. |
| Decide in your own system who should exist in TatvaOS, and send only those. | Feed an unfiltered list straight in — every person created counts against the organisation's plan. |
Treat 409 as success and re-run safely. | Build a one-shot import that cannot be run twice. |
8. What is not in this version
Stated so you can plan around it rather than discover it.
| Not yet | What it will be |
|---|---|
| Reading people | A list and lookup call, so your system can reconcile rather than re-send. |
| Suspending and offboarding | Ending someone's access when they leave, which is the other half of a joiners feed. |
| Bulk requests | Many people in one request. Until then: one request each, within the rate limit. |
| Updating a person | Changing a name or department through the API. Today that is the console. |
Each of those is a decision about what a stolen key could do, which is why none of them has been added quietly.