# Welcome

TNID API documentation: authenticate as a user or a company, then manage identity, connections, subscriptions, 10DLC and webhooks over two GraphQL servers.

TNID makes creating your personal and business identity a breeze. Connect with people and companies you care about, and manage your preferences with them in one simple to use interface.

### Document Formatting <a href="#document-formatting" id="document-formatting"></a>

We generally format our API documents in the following manner:

1. Overview: the purpose of the end-point
2. Authentication: how to authenticate to the end-point
3. Method: details regarding the API method and examples.

All TNID APIs are GraphQL based. Throughout the documentation, we use common HTTP features such as HTTP verbs and HTTP status codes. Requests must be made via HTTPS (calls over plain HTTP will fail).

Happy coding!

### Jump right in!

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Company Authentication</strong></td><td>Learn how to login as a Company and use features available to businesses.</td><td></td><td></td><td></td></tr><tr><td><strong>User Authentication</strong></td><td>Login as a User and use features available to individuals, including spam reporting.</td><td></td><td></td><td></td></tr></tbody></table>

### For developers and AI assistants

* [API Quick Reference](/api-quick-reference): environments, authentication, every feature area, webhooks and pagination on one page.
* [AI Assistants & MCP](/ai-assistants-and-mcp): connect Claude, Cursor, VS Code or Codex to these docs, or ask the docs a question from inside the API.
* [API Reference](https://docs.tnid.com/api-reference/): every query, mutation, type and enum, generated from the API source.
* [Changelog](/changelog): what changed in the API and in these docs.


# API Quick Reference

One-page summary of the TNID API: environments, authentication, the two GraphQL servers, every feature area, webhooks and pagination. Written for developers and AI coding assistants.

This page condenses the TNID API into a single read. Where this site has a guide page, it is linked. Where an operation exists in the API but has no guide here yet, the row points at the [**API Reference**](https://docs.tnid.com/api-reference/), which is regenerated from the API source and documents every query, mutation, type and enum with request, variables and response examples.

For AI assistants: this site is served as Markdown at `https://docs.tnid.com/llms.txt` and `https://docs.tnid.com/llms-full.txt`, any page can be fetched by appending `.md` to its URL, and a read-only MCP server is available at `https://docs.tnid.com/~gitbook/mcp`. See [AI Assistants & MCP](/ai-assistants-and-mcp).

## What TNID is

TNID is an identity and consent platform for people and businesses. A **user** owns a personal profile (verified emails, phone numbers, addresses) and decides which companies may connect with them and message them on which channels. A **company** owns a business profile, connects with other companies (B2B) and with people (B2C), manages subscriptions and opt-outs, registers 10DLC brands and campaigns, and receives webhooks when any of that changes.

## Environments

| Environment           | API host                                                                            | Web app                           |
| --------------------- | ----------------------------------------------------------------------------------- | --------------------------------- |
| Staging               | `https://api.staging.v2.tnid.com`                                                   | `https://app.staging.v2.tnid.com` |
| Zero (pre-production) | `https://api.zero.v2.tnid.com`                                                      | `https://app.zero.v2.tnid.com`    |
| Production            | Provided by TSG when your integration is approved. Contact <support@tsgglobal.com>. | `https://app.tnid.com`            |

All requests must use HTTPS. Accounts are invite-only at present. Rate limits are applied per token; the current values are not published, contact <support@tsgglobal.com> for your account's limits.

## Two GraphQL servers, one auth service

| Path                         | Who calls it                                                      | Token                |
| ---------------------------- | ----------------------------------------------------------------- | -------------------- |
| `POST /user`                 | A person acting as themselves                                     | User access token    |
| `POST /company`              | Software acting for a company                                     | Company access token |
| `POST /auth/create-user-otp` | Start a user login (also accepted as `/auth/create_user_otp`)     | none                 |
| `POST /auth/token`           | Exchange an OTP (user) or client credentials (company) for tokens | none                 |
| `POST /auth/refresh-token`   | Exchange a refresh token for a new token pair                     | none                 |

The user and company schemas overlap in naming but are separate. A query that exists on `/company` (for example `b2bConnections`) is not available on `/user`, and vice versa.

## Authentication

| Flow                           | Steps                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User, by phone or email        | 1. `POST /auth/create-user-otp` with `{"telephone_number": "..."}` or `{"email": "..."}`; add `first_name`, `last_name`, `email` to create a new user. Response includes `expires_at`, `next_otp_at`, `remaining_otp_attempts`. 2. `POST /auth/token` with `{"telephone_number"` or `"email", "otp_code"}`. Response: `access_token`, `refresh_token`. See [User Authentication](/authentication/user-authentication). |
| Company, by client credentials | Create a company as a user with `provisionCompanyClientSecret: true`, or mint a secret later with `createCompanyClientSecret` or in the web app. Then `POST /auth/token` with `{"client_id", "client_secret"}`. See [Company Authentication](/authentication/company-authentication).                                                                                                                                  |
| Refresh                        | `POST /auth/refresh-token` with `{"refresh_token"}`. Access tokens last 7 days, refresh tokens 30 days. See [Refresh Token](/authentication/refresh-token).                                                                                                                                                                                                                                                            |

Send the token on every GraphQL call as `Authorization: Bearer <access_token>`. Client secrets are shown once at creation; store them in a secret manager and rotate by creating a new secret and deleting the old one (`clientSecrets`, `createClientSecret`, `deleteClientSecret` on `/company`).

## Conventions

* Requests are JSON over `POST`; GraphQL bodies are `{"query": "...", "variables": {...}}`.
* Custom scalars: `JSON` (free-form metadata), `NaiveDateTime` (`2026-01-15T09:00:00`), `Date`, `Time`. IDs are UUIDs.
* Most list queries take `limit` and return a plain array. Every list that can grow also has a `paginated…` twin (for example `paginatedB2bConnections`) that takes `limit` and `cursor` and returns `paginationInfo { limit nextCursor }` plus `records`; page until `nextCursor` is null. 10DLC lists use `page`/`pageSize` instead. See [Pagination](/pagination).
* Profile fields carry visibility settings (`CompanyVisibilityType`, `UserVisibilityType`); what a caller can see depends on their relationship to the record.
* Connection and subscription requests follow one pattern: `create…Request` → the other side sees it in `received…Requests` → `respond…Request` with `ACCEPT` or `REJECT` → it appears in `…Connections`. The sender can `revoke…Request` while pending.
* Unauthenticated calls return HTTP 401 with `{"error": {"summary": "Authorization failed…"}}`. Validation failures return 400 with a `message`.

## Feature map

Coverage column: **Guide** means a walkthrough exists on this site; **Reference** means the operation is documented only in the API Reference so far.

### `/user` server

| Area                      | Key operations                                                                                                                                                                                                         | Coverage                                                                                                                                                                                                                               |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Profile                   | `currentUser`, `updateUser`                                                                                                                                                                                            | Guide: [Update User profile](/user/update-user-profile)                                                                                                                                                                                |
| Contact details           | `createUserEmail`, `createUserEmailVerificationCode`, `verifyUserEmail`, `setPrimaryUserEmail`, `removeUserEmail`; same set for telephone numbers; `createUserAddress`, `updateUserAddress`, `removeUserAddress`       | Reference: [emails](https://docs.tnid.com/api-reference/001_user/002_email), [addresses](https://docs.tnid.com/api-reference/001_user/003_address), [phone numbers](https://docs.tnid.com/api-reference/001_user/004_telephone_number) |
| People (C2C) connections  | `createC2cConnectionRequest`, `createC2cInvite`, `pendingC2cConnectionRequests`, `receivedC2cConnectionRequests`, `respondC2cConnectionRequest`, `revokeC2cConnectionRequest`, `c2cConnections`, `removeC2cConnection` | Guide: [Company Features](/user/company-features) (C2C pages)                                                                                                                                                                          |
| Company (C2B) connections | `createC2bConnectionRequest`, `pendingC2bConnectionRequests`, `receivedB2cConnectionRequests`, `respondB2cConnectionRequest`, `revokeC2bConnectionRequest`, `removeC2bConnection`, `b2cConnections`                    | Guide: [Connections](/user/connections)                                                                                                                                                                                                |
| Subscriptions             | `respondB2cSubscriptionRequest`, `upsertB2cSubscription`, `removeB2cSubscription`, `b2cSubscriptions`, `pendingB2cSubscriptionRequests`                                                                                | Guide: [Respond to B2C Subscription Request](/user/company-features/respond-to-b2c-subscription-request), [Remove B2C Subscription](/user/company-features/remove-b2c-subscription)                                                    |
| Spam reporting            | `createSpamReport`, `spamReports`, `paginatedSpamReports`                                                                                                                                                              | Guide: [Spam Reporting Features](/user/spam-reporting-features)                                                                                                                                                                        |
| Groups                    | `groups`, `group`, `userGroups`, `userGroupMembers`, `createGroup`, `updateGroup`, `leaveGroup`, `updateGroupUserMemberRole`, `removeGroupUserMember`, invites and join requests                                       | Guide: [Group Features](/group-features)                                                                                                                                                                                               |
| Company management        | `createCompany`, `createCompanyClientSecret`, `deleteCompany`, `switchableCompanies`, `setupInvitationFromToken`                                                                                                       | Guide: [Create company profile](/user/company-features/create-company-profile); rest Reference: [company management](https://docs.tnid.com/api-reference/001_user/010_company_management)                                              |
| Search                    | `users`, `user`, `userByUsername`, `globalSearch` (users, companies and groups in one list; 3-character minimum)                                                                                                       | Guide: [Search User (People)](/user/search-user-people); `globalSearch` Reference: [users and search](https://docs.tnid.com/api-reference/001_user/011_users_and_search)                                                               |
| Documents and AI tasks    | `prepareFileUpload`, `prepareDocumentUpload`, `confirmDocumentUpload`, folders, tags, trash, `askUserDocumentQuestion`, AI tasks (`createUserAiTask`, `runUserAiTask`, `aiTaskRuns`)                                   | Reference: [documents](https://docs.tnid.com/api-reference/001_user/012_documents)                                                                                                                                                     |
| Notifications             | `notifications`, `recentNotifications`, `unreadNotificationCount`, mark-as-read mutations, notification settings per group and per B2C connection                                                                      | Reference: [notifications](https://docs.tnid.com/api-reference/001_user/013_notifications)                                                                                                                                             |
| Forms                     | `receivedFormSubmissionRequests`, `rejectFormSubmissionRequest`                                                                                                                                                        | Reference: [forms](https://docs.tnid.com/api-reference/001_user/014_forms)                                                                                                                                                             |
| Account                   | `currentUserSettings`, `updateUserSettings`, `dashboardStats`, `onboardingStatus`, `completeOnboardingStep`, `createSupportRequest`, `interests`, `pendingActionCounts`                                                | Reference: [account](https://docs.tnid.com/api-reference/001_user/015_account)                                                                                                                                                         |
| Docs assistant            | `askDocsQuestion` answers a natural-language question from this documentation                                                                                                                                          | Reference: [assistant](https://docs.tnid.com/api-reference/001_user/016_assistant)                                                                                                                                                     |

### `/company` server

| Area                               | Key operations                                                                                                                                                                                                                                                                                                                  | Coverage                                                                                                                                                                                                                                        |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Profile                            | `currentCompany`, `updateCompany`, `companies` (search), `createB2bInvite`                                                                                                                                                                                                                                                      | Guide: [Search Companies](/company/search-companies), [Invite Company](/company/invite-company), [Update Company Profile](/company/update-company-profile)                                                                                      |
| Contact details                    | Create, verify, update, remove company emails, telephone numbers and addresses                                                                                                                                                                                                                                                  | Reference: [emails](https://docs.tnid.com/api-reference/002_company/002_email), [addresses](https://docs.tnid.com/api-reference/002_company/003_address), [phone numbers](https://docs.tnid.com/api-reference/002_company/004_telephone_number) |
| 10DLC                              | API keys (`addTenDlcApiKey`, `authorizeTenDlcApiKey`), brands (`registerTenDlcBrandAsDraft`, `registerAndSubmitTenDlcBrand`, `updateAndSubmitTenDlcBrand`, `tenDlcBrands`), campaigns (`saveTenDlcCampaignAsDraft`, `registerAndSubmitTenDlcCampaign`, `registerTenDlcCampaignWhenReady`, `tenDlcCampaignWithTelephoneNumbers`) | Guide: [10DLC Brands](/10dlc/brands), [10DLC Campaigns](/10dlc/campaigns)                                                                                                                                                                       |
| B2B connections                    | `createB2bConnectionRequest`, `b2bConnections`, `pendingB2bConnectionRequests`, `receivedB2bConnectionRequests`, `respondB2bConnectionRequest`, `revokeB2bConnectionRequest`, `removeB2bConnection`                                                                                                                             | Guide: [B2B Features](/company/b2b-features)                                                                                                                                                                                                    |
| B2C connections                    | `createB2cConnectionRequest`, `createB2cInvite`, `b2cConnections`, `pendingB2cConnectionRequests`, `receivedC2bConnectionRequests`, `respondC2bConnectionRequest`, `revokeB2cConnectionRequest`, `removeB2cConnection`                                                                                                          | Guide: [B2C Features](/company/b2c-features)                                                                                                                                                                                                    |
| Subscriptions and topics           | `createB2cSubscriptionRequest`, `b2cSubscriptions`, `pendingB2cSubscriptionRequests`, `revokeB2cSubscriptionRequest`, `removeB2cSubscription`; topics: `b2cSubscriptionTopics`, `createB2cSubscriptionTopic`, `updateB2cSubscriptionTopic`, `removeB2cSubscriptionTopic`                                                        | Guide: [B2C Features](/company/b2c-features); topics Reference: [subscriptions](https://docs.tnid.com/api-reference/002_company/007_b2c_subscriptions)                                                                                          |
| Opt-out and opt-in                 | `optOutRequests`, `createOptOutRequest`, `generateSmsOptInQrCode`                                                                                                                                                                                                                                                               | Guide: [List Opt-Out Requests](/company/b2c-features/list-opt-out-requests), [Create Opt-out Request](/company/b2c-features/create-opt-out-request), [Generate SMS Opt-In QR Code](/company/b2c-features/generate-sms-opt-in-qr-code)           |
| People search                      | `users`, `user`, `userByUsername`, `globalSearch`                                                                                                                                                                                                                                                                               | Guide: [Search People](/company/b2c-features/search-people)                                                                                                                                                                                     |
| Documents and AI tasks             | Same document, folder, tag, trash and AI-task operations as the user server, scoped to the company                                                                                                                                                                                                                              | Reference: [documents](https://docs.tnid.com/api-reference/002_company/011_documents)                                                                                                                                                           |
| Forms                              | `createForm`, `createFormVersion`, `publishFormVersion`, `companyForms`, `createB2cFormSubmissionRequest`, `createB2bFormSubmissionRequest`, `formSubmissions`, `formSubmission`                                                                                                                                                | Reference: [forms](https://docs.tnid.com/api-reference/002_company/013_forms)                                                                                                                                                                   |
| Webhooks                           | `companyWebhooks`, `createCompanyWebhook`, `updateCompanyWebhook`, `removeCompanyWebhook`                                                                                                                                                                                                                                       | Guide: [Webhooks](/company/webhooks)                                                                                                                                                                                                            |
| Invitation links                   | `companyInvitationLinks`, `createCompanyInvitationLink`, `updateCompanyInvitationLink`, `deleteCompanyInvitationLink`                                                                                                                                                                                                           | Reference: [invitation links](https://docs.tnid.com/api-reference/002_company/015_invitation_links)                                                                                                                                             |
| Client secrets                     | `clientSecrets`, `createClientSecret`, `updateClientSecret`, `deleteClientSecret`                                                                                                                                                                                                                                               | Reference: [client secrets](https://docs.tnid.com/api-reference/002_company/016_client_secrets)                                                                                                                                                 |
| User imports                       | `importUsersFromCsv` (from a `prepareFileUpload` key; can create connections and request subscriptions in bulk), `userImports`, `userImport`                                                                                                                                                                                    | Reference: [user imports](https://docs.tnid.com/api-reference/002_company/017_user_imports)                                                                                                                                                     |
| Sentiment analysis                 | `reportExternalCommunication` (send message traffic for analysis; auto-subscribes or flags an unsubscribe and calls your webhook), `feedbackOnDetectedUnsubscribe`, `sentimentAnalysisResults`                                                                                                                                  | Reference: [sentiment analysis](https://docs.tnid.com/api-reference/002_company/003_message_sentiment_analysis)                                                                                                                                 |
| RCS and AI suggestions             | `companyRcsInformation`, `upsertCompanyRcsInformation`, `getAiCompanyProfileSuggestions`, `getAiRcsSuggestions`, `getAiFormSuggestionsFromUrl`, `getAiFormSuggestionsFromFile`                                                                                                                                                  | Reference: [RCS and AI suggestions](https://docs.tnid.com/api-reference/002_company/018_rcs_and_ai_suggestions)                                                                                                                                 |
| Notifications, settings, assistant | `notifications`, `unreadNotificationCount`, `companySettings`, `updateCompanySettings`, `companyDashboardStats`, `pendingActionCounts`, `askDocsQuestion`                                                                                                                                                                       | Reference: [notifications](https://docs.tnid.com/api-reference/002_company/012_notifications), [settings](https://docs.tnid.com/api-reference/002_company/019_settings)                                                                         |
| Reference data                     | `organizationTypes`, `verticalTypes`, `specialties`                                                                                                                                                                                                                                                                             | Guide: [List Organization Types](/company/list-organization-types), [List Vertical Types](/company/list-vertical-types)                                                                                                                         |

## Webhooks

Companies register webhooks with `createCompanyWebhook` on `/company`: a target `url`, an optional `bearerToken` TNID sends back in the `Authorization` header, optional `customHeaders`, and one boolean per event type:

| Flag                                              | Fires when                                                             |
| ------------------------------------------------- | ---------------------------------------------------------------------- |
| `receiveSubscriptionEvents`                       | A person subscribes, changes or removes a subscription to your company |
| `receiveOptOutEvents`                             | An opt-out is recorded                                                 |
| `b2bConnectionRequestReceivedWebhook`             | Another company asks to connect                                        |
| `b2bConnectionRequestResponseWebhook`             | A company responds to your request                                     |
| `b2cConnectionRequestResponseWebhook`             | A person responds to your connection request                           |
| `c2bConnectionRequestReceivedWebhook`             | A person asks to connect with you                                      |
| `b2cSubscriptionRequestResponseWebhook`           | A person responds to your subscription request                         |
| `companySocialNetworkStatusChangeWebhook`         | A social-network link on your profile changes verification status      |
| `b2bFormInstanceSubmissionRequestReceivedWebhook` | Another company asks you to fill in a form                             |

Sentiment analysis also posts to your webhook when it detects an unsubscribe: `{"external_communication_id": "...", "timestamp": "...", "action": "unsubscribe"}`. Confirm or deny it with `feedbackOnDetectedUnsubscribe`. Full guide: [Webhooks](/company/webhooks).

## Minimal examples

Request a login code, then a token (staging):

```bash
curl -X POST https://api.staging.v2.tnid.com/auth/create-user-otp \
  -H "Content-Type: application/json" \
  -d '{"telephone_number":"15555550100"}'

curl -X POST https://api.staging.v2.tnid.com/auth/token \
  -H "Content-Type: application/json" \
  -d '{"telephone_number":"15555550100","otp_code":"123456"}'
```

Authenticate as a company and search for companies:

```bash
TOKEN=$(curl -s -X POST https://api.staging.v2.tnid.com/auth/token \
  -H "Content-Type: application/json" \
  -d '{"client_id":"<client_id>","client_secret":"<client_secret>"}' | jq -r .access_token)

curl -X POST https://api.staging.v2.tnid.com/company \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"query":"query($name:String,$limit:Int){ companies(name:$name, limit:$limit){ id legalName brandName verified } }","variables":{"name":"Acme","limit":5}}'
```

Page through B2B connections:

```bash
curl -X POST https://api.staging.v2.tnid.com/company \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"query":"query($limit:Int,$cursor:String){ paginatedB2bConnections(limit:$limit, cursor:$cursor){ paginationInfo { limit nextCursor } records { id type connectedCompany { id legalName } } } }","variables":{"limit":50}}'
```

Pass `paginationInfo.nextCursor` back as `cursor` until it is null.

## Support and related links

* Support: <support@tsgglobal.com>
* [API Reference](https://docs.tnid.com/api-reference/), regenerated from the API source (queries, mutations, types, enums and input types under "Definitions")
* Guides: [Pagination](/pagination), [Webhooks](/company/webhooks), [10DLC Brands](/10dlc/brands), [10DLC Campaigns](/10dlc/campaigns), [Changelog](/changelog)
* Postman setup and schema introspection: [Accessing via Postman](/authentication/accessing-via-postman-graphql-schema-examples)
* Machine-readable copies of this site: `/llms.txt`, `/llms-full.txt`, `<page>.md`, MCP at `/~gitbook/mcp`


# AI Assistants & MCP

Connect Claude, Cursor, VS Code, Codex or any MCP client to the TNID documentation, and let your app ask the docs a question from inside the API.

TNID's documentation is available to AI coding assistants through the Model Context Protocol (MCP), an open standard that lets an assistant call outside tools. Connect the server below and your assistant can search these docs, read any page, and report documentation problems back to us instead of guessing from stale training data.

## The documentation MCP server

| Setting        | Value                                             |
| -------------- | ------------------------------------------------- |
| Server URL     | `https://docs.tnid.com/~gitbook/mcp`              |
| Transport      | Streamable HTTP                                   |
| Authentication | None. The server only reads public documentation. |
| Cost           | Free                                              |

The server covers both the guides and the API Reference section of this site. It exposes three tools:

| Tool                  | What it does                                                                                                               |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `searchDocumentation` | Full-text search across every page. Returns matching excerpts with titles and links.                                       |
| `getPage`             | Fetches the complete Markdown of one page by its URL, for example `https://docs.tnid.com/company/search-companies`.        |
| `sendFeedback`        | Lets the assistant report outdated, contradictory or missing content on a page. Reports go to the TNID documentation team. |

{% hint style="info" %}
This server covers documentation only. It cannot log in, create connections, send subscription requests or call the TNID API. Your tokens and client secrets never pass through it. To act on the platform, your code still calls the `/user` and `/company` GraphQL servers described in this documentation.
{% endhint %}

## Connect your assistant

{% tabs %}
{% tab title="Claude Code" %}
Run once in your terminal:

```bash
claude mcp add --transport http tnid-docs https://docs.tnid.com/~gitbook/mcp
```

Then ask, for example, "Using the TNID docs, write a client that authenticates as a company and lists pending B2B connection requests."
{% endtab %}

{% tab title="Claude.ai / Desktop" %}
Open **Settings → Connectors → Add custom connector**, name it "TNID docs", and paste `https://docs.tnid.com/~gitbook/mcp` as the URL. No authentication is needed. Enable it in a chat from the tools menu.
{% endtab %}

{% tab title="Cursor" %}
Add to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` for all projects):

```json
{
  "mcpServers": {
    "tnid-docs": {
      "url": "https://docs.tnid.com/~gitbook/mcp"
    }
  }
}
```

{% endtab %}

{% tab title="VS Code" %}
Add to `.vscode/mcp.json` in your workspace, then start the server from the MCP view:

```json
{
  "servers": {
    "tnid-docs": {
      "type": "http",
      "url": "https://docs.tnid.com/~gitbook/mcp"
    }
  }
}
```

{% endtab %}

{% tab title="Codex" %}

```bash
codex mcp add tnid-docs --url https://docs.tnid.com/~gitbook/mcp
```

{% endtab %}

{% tab title="Other clients" %}
Any MCP client that supports streamable HTTP can connect. Point it at `https://docs.tnid.com/~gitbook/mcp` with no bearer token. `stdio` and SSE transports are not offered.
{% endtab %}
{% endtabs %}

## Other machine-readable formats

| Resource                          | URL                                                                                                        | Use it for                                                                          |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Index of all pages with summaries | `https://docs.tnid.com/llms.txt`                                                                           | Give an assistant a map of the docs                                                 |
| Every page in one file            | `https://docs.tnid.com/llms-full.txt`                                                                      | Load the whole guide into context                                                   |
| Any single page                   | Append `.md` to its URL, e.g. `https://docs.tnid.com/getting-started-guide.md`                             | Fetch one page without HTML                                                         |
| API Reference                     | [docs.tnid.com/api-reference](https://docs.tnid.com/api-reference/) (every page also available with `.md`) | Every query, mutation, type and enum, generated from the API source                 |
| One-page API summary              | [API Quick Reference](/api-quick-reference)                                                                | Environments, authentication, feature map, webhooks and pagination in a single read |

## Ask the docs from inside the API

Both GraphQL servers expose a mutation that answers a natural-language question from this documentation and names the pages it drew on. It is useful for in-app help and for agents that already hold a TNID token:

```graphql
mutation ($question: String!) {
  askDocsQuestion(question: $question) {
    answer
  }
}
```

A user token is answered from the user half of the reference, a company token from the company half. See the API Reference: [user assistant](https://docs.tnid.com/api-reference/001_user/016_assistant), [company assistant](https://docs.tnid.com/api-reference/002_company/020_assistant).

## What to expect

* Answers are grounded in the published documentation. If a page is wrong, the assistant will be wrong too; the `sendFeedback` tool exists so it can tell us.
* The assistant still needs your credentials to run code against the API. Keep client secrets and tokens in environment variables, never in prompts or chat history.
* Prompts that work well: "Search the TNID docs for how B2C subscription requests are accepted and write the webhook handler", "Which server, /user or /company, exposes createSpamReport?", "Compare the plain and paginated variants of b2bConnections."

Questions or ideas for the MCP server? Email <support@tsgglobal.com>.


# Pagination

How TNID list queries page: plain lists with limit, cursor-paginated twins with paginationInfo and nextCursor, and page-number lists for 10DLC.

TNID has three list shapes. Pick by name.

## Plain lists: `limit`

Queries such as `b2bConnections`, `spamReports`, `companies` and `users` take a `limit` and return a plain array. They are fine for small result sets and for the examples in this guide. There is no way to fetch the next batch.

## Cursor lists: `paginated…` queries

Every list that can grow has a cursor-paginated twin whose name starts with `paginated`, for example `paginatedB2bConnections`, `paginatedSpamReports`, `paginatedReceivedC2cConnectionRequests`, `paginatedUserGroups`, `paginatedClientSecrets` and `paginatedSentimentAnalysisResults`. Documents, forms, AI tasks and group members are cursor-paginated by default.

They take `limit` and `cursor` and return `paginationInfo` plus `records`:

```graphql
query ($limit: Int, $cursor: String) {
  paginatedB2bConnections(limit: $limit, cursor: $cursor) {
    paginationInfo {
      limit
      nextCursor
    }
    records {
      id
      type
      connectedCompany {
        id
        legalName
      }
    }
  }
}
```

First call with `{"limit": 50}`. Each response carries `paginationInfo.nextCursor`; pass it back as `cursor` to get the next page. When `nextCursor` is `null` you have everything. The default `limit` is 20.

```python
cursor = None
while True:
    page = client.execute(query, {"limit": 50, "cursor": cursor})["paginatedB2bConnections"]
    for record in page["records"]:
        handle(record)
    cursor = page["paginationInfo"]["nextCursor"]
    if cursor is None:
        break
```

Cursors are opaque strings. Do not store them across sessions or build them by hand; restart from the first page instead.

## Page-number lists: 10DLC

`tenDlcBrands` and `tenDlcCampaigns` use `page` and `pageSize` and return `page`, `totalRecords` and `records`. Increment `page` until `page * pageSize >= totalRecords`. See [10DLC Brands](/10dlc/brands).

## Finding the paginated variant

The [API Reference](https://docs.tnid.com/api-reference/) lists each plain query next to its paginated twin, and the [Queries](https://docs.tnid.com/api-reference/003_definitions/queries) definition page shows every argument.


# Getting Started Guide

The five steps from nothing to a working TNID integration: user login, company creation, company credentials, first calls, and where to point requests.

## What you will use

* **Auth endpoints:** `/auth/create-user-otp`, `/auth/token`, `/auth/refresh-token`
* **GraphQL servers:** `/user` (for people) and `/company` (for businesses)
* **Hosts:** `https://api.staging.v2.tnid.com` for staging, `https://api.zero.v2.tnid.com` for the zero (pre-production) environment. See [API Quick Reference](/api-quick-reference) for the full picture.

You can try everything with Postman first: see [Accessing via Postman](/authentication/accessing-via-postman-graphql-schema-examples).

***

## 1) Create (or claim) your individual profile

Think of this as getting your user login.

**Step A: request a one-time code (OTP).** Send your phone number or email (and, if you are creating a new user, first name, last name and email) to `/auth/create-user-otp`. You receive an OTP.

**Step B: exchange the OTP for a Bearer token.** Send your `telephone_number` (or `email`) and `otp_code` to `/auth/token`. You get an `access_token` that lets you call the `/user` GraphQL API, plus a `refresh_token`. Save both securely.

Details and code: [User Authentication](/authentication/user-authentication).

**Optional: update your user profile.** With your user token, call the `updateUser` mutation on `/user` to set `firstName`, `lastName`, `username` or `aboutMe`. See [Update User profile](/user/update-user-profile).

***

## 2) Create your company (as that user)

Once you have a user token, you can create a company and mint credentials for it.

**Step A: create the company profile.** Call the `createCompany` mutation on `/user` with the basics (`legalName`, `profileName`, optional `brandName`, `taxId`, and so on). If you include `provisionCompanyClientSecret: true`, TNID also returns a `clientId` and `clientSecret` for company-level API calls. See [Create company profile](/user/company-features/create-company-profile).

**Step B (alternative): generate company credentials later.** Call [Create Company Client Secret](/user/company-features/create-company-client-secret) or use Client Secrets in the TNID app, then exchange `client_id` and `client_secret` at `/auth/token` for a company Bearer token. Use that token with `/company`. See [Company Authentication](/authentication/company-authentication).

**Optional: update your company profile.** Use the `updateCompany` mutation with your company token. See [Update Company Profile](/company/update-company-profile).

***

## 3) Common first calls after setup

**Find companies** (to connect with, or to avoid duplicates): query `companies` by name, tax ID, email, telephone number or webpage on `/company`. See [Search Companies](/company/search-companies).

**Invite another company or request a B2B connection:**

* Invite by details (legal name plus a representative's email) with `createB2bInvite`. See [Invite Company](/company/invite-company).
* Or send a connection request by `invitedCompanyId` with `createB2bConnectionRequest`. See [Send B2B Connection Request](/company/b2b-features/send-b2b-connection-request).

**Search for people and see your consumer connections:** use `users` and `b2cConnections` on `/company`. See [Search People](/company/b2c-features/search-people) and [B2C Features](/company/b2c-features).

**User-side spam reporting:** as a user, call `createSpamReport` and `spamReports` on `/user` so your customers can report unwanted communications. See [Spam Reporting Features](/user/spam-reporting-features).

**Get notified instead of polling:** register a company webhook so TNID calls you when connection requests, subscriptions and opt-outs change. See Webhooks.

**Register for 10DLC:** brands and campaigns are submitted through TNID. See [10DLC Brands](/10dlc/brands).

***

## 4) Where to point your requests

| Purpose                                       | Path                         |
| --------------------------------------------- | ---------------------------- |
| People GraphQL                                | `POST /user`                 |
| Companies GraphQL                             | `POST /company`              |
| Request an OTP                                | `POST /auth/create-user-otp` |
| Exchange OTP or client credentials for tokens | `POST /auth/token`           |
| Refresh tokens                                | `POST /auth/refresh-token`   |

Prefix each path with the environment host, for example `https://api.staging.v2.tnid.com/company`.

***

## 5) Quick mental model

1. **Become a user** (OTP, token, `/user`).
2. **Create your company** as that user and **mint company credentials**.
3. **Act as the company** (company token, `/company`) for B2B and B2C connections, subscriptions, profile updates, invites, webhooks and 10DLC.

Every query and mutation, including the ones this guide does not cover, is documented in the [API Reference](https://docs.tnid.com/api-reference/).


# Company Authentication

Create a company client secret in the TNID app, then exchange client\_id and client\_secret at /auth/token for a company access token.

Learn how to get your Company TNID client key and secret to authenticate into the GraphQL API.

<figure><img src="https://218732174-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfPazwA5iPiTPgaPvkQZx%2Fuploads%2FuwD0yvcflCzURsLbk0vD%2FScreenshot%202024-09-30%20at%201.43.43%E2%80%AFPM.png?alt=media&#x26;token=1f241e8f-689d-4972-aea3-518e2593b360" alt=""><figcaption><p>Screenshot of Client Secrets section in GUI</p></figcaption></figure>

Company users of TNID are able to generate Client Secrets so that they can access the API. To create a Company, first register as an Individual, and then create a Company associated with your Individual profile. Then, assume the role of your Company and click "New Client Secret" in order to create your credentials. You can also create the secret through the API: pass `provisionCompanyClientSecret: true` when [creating the company](/user/company-features/create-company-profile), or call [Create Company Client Secret](/user/company-features/create-company-client-secret) later.

{% hint style="danger" %}
You are only allowed to view your Client Secret value at creation, so please ensure you save it someplace safe (e.g. 1Password or your environment variables). You will not be able to see it once you close the pop-up (however, you can always create a new Client Secret).
{% endhint %}

{% hint style="warning" %}
Client secrets do not expire. Access tokens are valid for 7 days and refresh tokens for 30 days (see [Refresh Token](/authentication/refresh-token)). If you believe a secret has been compromised, create a new one and delete the old one.
{% endhint %}

### Generate Your Bearer Token

The token endpoint is available in each environment:

| Environment           | Token endpoint                               |
| --------------------- | -------------------------------------------- |
| Staging               | `https://api.staging.v2.tnid.com/auth/token` |
| Zero (pre-production) | `https://api.zero.v2.tnid.com/auth/token`    |

Simply pass the client\_id and client\_secret data to generate your Bearer token for future API requests:

```json
{
   "client_id": "CLIENT_ID_VALUE",
   "client_secret": "CLIENT_SECRET_VALUE"
}
```

The response contains an `access_token` and a `refresh_token`. Use the `access_token` as `Authorization: Bearer <access_token>` to interact with the GraphQL API at `/company`.

### Example Company authentication

{% tabs %}
{% tab title="Python" %}

```python
import requests

def get_bearer_token(client_id, client_secret):
    url = "https://api.staging.v2.tnid.com/auth/token"

    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }

    data = {
        "client_id": client_id,
        "client_secret": client_secret
    }

    response = requests.post(url, headers=headers, data=data)

    if response.status_code == 200:
        token_data = response.json()
        return token_data.get("access_token")
    else:
        raise Exception(f"Failed to retrieve token: {response.status_code} {response.text}")

# Example usage:
client_id = "your_client_id"          # Replace with your actual client ID
client_secret = "your_client_secret"  # Replace with your actual client secret

token = get_bearer_token(client_id, client_secret)
print("Bearer Token:", token)
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -X POST https://api.staging.v2.tnid.com/auth/token \
  -H "Content-Type: application/json" \
  -d '{"client_id":"<client_id>","client_secret":"<client_secret>"}'
```

{% endtab %}
{% endtabs %}


# User Authentication

Request a one-time code by phone number or email, then exchange it at /auth/token for a user access token.

Learn how to get a TNID user access token to authenticate into the `/user` GraphQL API.

Users of TNID (as opposed to Companies) authenticate with a one-time code (OTP) delivered to their telephone number or email address.

{% hint style="warning" %}
Access tokens are valid for 7 days and refresh tokens for 30 days (see [Refresh Token](/authentication/refresh-token)). There are no passwords to rotate; if a token is compromised, stop using it and request a new OTP.
{% endhint %}

### Generate Your Access Token

The OTP endpoint is available in each environment:

| Environment           | OTP endpoint                                           |
| --------------------- | ------------------------------------------------------ |
| Staging               | `https://api.staging.v2.tnid.com/auth/create-user-otp` |
| Zero (pre-production) | `https://api.zero.v2.tnid.com/auth/create-user-otp`    |

The path is also accepted spelled `/auth/create_user_otp`.

**Step 1.** Make a request to `/auth/create-user-otp` with the `telephone_number` or `email` of an existing user. To create a new user, include the optional profile fields:

```json
{
   "telephone_number": "13024343433",
   "first_name": "John",
   "last_name": "Doe",
   "email": "email@address.com"
}
```

The response tells you when the code expires and how many attempts remain:

```json
{
  "expires_at": "2026-01-15T09:00:01.000000Z",
  "next_otp_at": "2026-01-15T09:00:00",
  "otp_status": "OTP sent",
  "remaining_otp_attempts": 5
}
```

**Step 2.** Make a request to `/auth/token` with the same `telephone_number` (or `email`) and the received `otp_code`:

```json
{
   "telephone_number": "13024343433",
   "otp_code": "698125"
}
```

The response contains an `access_token` and a `refresh_token`. Use the `access_token` as `Authorization: Bearer <access_token>` to interact with the GraphQL API at `/user`.

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
import requests

BASE = "https://api.staging.v2.tnid.com"
HEADERS = {"Content-Type": "application/x-www-form-urlencoded"}

# Step one: request an OTP for an existing user. The user receives the code by SMS (or email if you pass "email").
def request_otp(user_phone_number):
    response = requests.post(f"{BASE}/auth/create-user-otp", headers=HEADERS,
                             data={"telephone_number": user_phone_number})
    if response.status_code != 200:
        raise Exception(f"Failed to request OTP: {response.status_code} {response.text}")
    print(f"OTP requested: {response.text}")

# Step two: exchange the OTP for tokens.
def get_bearer_token(user_phone_number, otp):
    response = requests.post(f"{BASE}/auth/token", headers=HEADERS,
                             data={"telephone_number": user_phone_number, "otp_code": otp})
    if response.status_code != 200:
        raise Exception(f"Failed to retrieve token: {response.status_code} {response.text}")
    return response.json().get("access_token")

# Creating a new user: same endpoint, with profile fields. Then request the OTP and token as above.
def create_new_user(phone_number, first_name, last_name, email):
    response = requests.post(f"{BASE}/auth/create-user-otp", headers=HEADERS,
                             data={"telephone_number": phone_number, "first_name": first_name,
                                   "last_name": last_name, "email": email})
    if response.status_code != 200:
        raise Exception(f"Failed to create new user: {response.status_code} {response.text}")
    print(f"User created: {response.text}")

phone = "14075554530"
request_otp(phone)
otp = input("Enter the OTP you received: ")
print("Bearer Token:", get_bearer_token(phone, otp))
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -X POST https://api.staging.v2.tnid.com/auth/create-user-otp \
  -H "Content-Type: application/json" \
  -d '{"telephone_number":"14075554530"}'

curl -X POST https://api.staging.v2.tnid.com/auth/token \
  -H "Content-Type: application/json" \
  -d '{"telephone_number":"14075554530","otp_code":"698125"}'
```

{% endtab %}
{% endtabs %}


# Refresh Token

Exchange a refresh token at /auth/refresh-token for a new access token (7 days) and refresh token (30 days).

Access tokens have a limited lifetime. To keep calling the API without asking the user (or re-sending client credentials), exchange the refresh token for a new pair.

* Make a `POST` request to `/auth/refresh-token` with the `refresh_token` you received from `/auth/token`.
* The response contains a new `access_token` (7 days validity) and a new `refresh_token` (30 days validity).
* An access token can be used to access restricted resources. A refresh token can only be used to refresh tokens.
* The same endpoint works for user tokens and company tokens.

Request:

```json
{
   "refresh_token": "eyJ..."
}
```

Response:

```json
{
   "access_token": "eyJ...",
   "refresh_token": "eyJ..."
}
```

```bash
curl -X POST https://api.staging.v2.tnid.com/auth/refresh-token \
  -H "Content-Type: application/json" \
  -d '{"refresh_token":"<refresh_token>"}'
```


# Accessing via Postman / GraphQL Schema examples

Browse the live GraphQL schema in Postman with your Bearer token, or fetch it programmatically with the gql Python client.

## Postman

Once you have generated your Bearer token, you can access the GraphQL schema easily using Postman. Simply take your generated token, and create a new request using the GraphQL feature:

<figure><img src="https://218732174-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfPazwA5iPiTPgaPvkQZx%2Fuploads%2FvLHFwqwTq4ERIzyWwC2Q%2Fimage.png?alt=media&#x26;token=6f47b718-da6d-41aa-9c4b-563ed5e1b6a6" alt=""><figcaption><p>Select GraphQL from the dropdown.</p></figcaption></figure>

<figure><img src="https://218732174-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfPazwA5iPiTPgaPvkQZx%2Fuploads%2F2dROSXRXFbIztkzNl1mn%2FScreenshot%202024-10-16%20at%208.18.19%E2%80%AFAM.png?alt=media&#x26;token=0b525ce5-d3c6-48a0-ad9d-a9422c10ad78" alt=""><figcaption><p>Authenticate using your generated Bearer token.</p></figcaption></figure>

Load the GraphQL schema by entering the URL (`https://api.staging.v2.tnid.com/company` for the company endpoints, or `/user` for the user endpoints), and then press "Use GraphQL Introspection" on the Schema tab.

<figure><img src="https://218732174-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfPazwA5iPiTPgaPvkQZx%2Fuploads%2F7uUbUnlsrZF76S7rYped%2FScreenshot%202024-10-16%20at%208.20.13%E2%80%AFAM.png?alt=media&#x26;token=63f2ffb6-eec2-400b-a6e9-ca581f5793c8" alt=""><figcaption><p>Press "Use GraphQL Introspection" to load the schema.</p></figcaption></figure>

<figure><img src="https://218732174-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfPazwA5iPiTPgaPvkQZx%2Fuploads%2FP2FchM9frYc6trduqiuJ%2FScreenshot%202024-10-16%20at%208.20.26%E2%80%AFAM.png?alt=media&#x26;token=e1c8e760-8fc5-4000-801b-f0d96d6dcdfb" alt=""><figcaption></figcaption></figure>

The schema should now be accessible via Postman. A rendered version of the same schema, with request and response examples for every operation, is published in the [API Reference](https://docs.tnid.com/api-reference/).

## Example Code to access GraphQL Schema

{% tabs %}
{% tab title="Python" %}

```python
import pprint
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def get_full_company_endpoint_schema(bearer_token):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/company",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    # fetch_schema_from_transport=True makes gql run the introspection query for you
    client = Client(transport=transport, fetch_schema_from_transport=True)

    # Any valid query triggers the schema fetch
    query = gql("query ($name: String, $limit: Int) { companies(name: $name, limit: $limit) { id legalName } }")

    try:
        response = client.execute(query, {"name": "schema query", "limit": 1})
        print(f"Response OK: {response}")
        print("Schema: ")
        pprint.pp(client.introspection)
        return response
    except Exception as e:
        print(f"Exception: {e}")


def get_full_user_endpoint_schema(bearer_token):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/user",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql("query ($limit: Int) { spamReports(limit: $limit) { id } }")

    try:
        response = client.execute(query, {"limit": 1})
        print(f"Response OK: {response}")
        print("Schema: ")
        pprint.pp(client.introspection)
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_company_token"
get_full_company_endpoint_schema(token)

# OR
# token = "your_user_token"
# get_full_user_endpoint_schema(token)
```

{% endtab %}
{% endtabs %}


# Search Companies

Search for one or more companies based on name, tax ID, website, or other fields.

Search for one or more companies based on name, tax ID, website, or other fields.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

This endpoint will return a list of companies matching the query and in case queried props are visible to the querier, it will be possible to query by:

* ID
* Legal name (with name field)
* Brand name (with name field)
* Profile name (with name field)
* TaxID
* Telephone number
* Email
* Webpage (social media or other)

Each result will return a list of companies. It is possible to pass a combination of queryable props.

```graphql
query (
    $id: ID
    $name: String
    $taxId: String
    $email: String
    $telephoneNumber: String
    $webpage: String
    $limit: Int
    $metadata: JSON
  ) {
    companies (
      id: $id
      name: $name
      taxId: $taxId
      email: $email
      telephoneNumber: $telephoneNumber
      webpage: $webpage
      limit: $limit
      metadata: $metadata
    ) {
      id
      legalName
      brandName
      profileName
      taxId
      yearFounded
      aboutUs
      metadata
      verified
      logoUrl
      verticalType {
        id
        displayName
        description
      }
      organizationType {
        id
        displayName
      }
      addresses {
        city
        country
        state
        street
        types
        zipCode
      }
      emails {
        email
      }
      telephoneNumbers {
        number
      }
      socialNetworks {
        type
        url
      }
      webpages {
        type
        url
      }
      specialties {
        description
        title
      }
      subscriptionTopics {
        id
        name
      }
      subscriptionSettings {
        enable_email
        enable_sms
        enable_voice
      }
    }
  }
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def search_companies(bearer_token, query_name=None, tax_id=None, email=None, phone_number=None, webpage=None, limit_count=10):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/company",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql(
        """
        query (
            $name: String
            $taxId: String
            $email: String
            $telephoneNumber: String
            $webpage: String
            $limit: Int
        ) {
            companies (
                name: $name
                taxId: $taxId
                email: $email
                telephoneNumber: $telephoneNumber
                webpage: $webpage
                limit: $limit
            ) {
                id
                legalName
                brandName
                profileName
                taxId
            }
        }
        """
    )

    params = {"name": query_name, "taxId": tax_id, "email": email, "telephoneNumber": phone_number,
              "webpage": webpage, "limit": limit_count}

    try:
        response = client.execute(query, params)
        print(f"Response OK when searching companies: {response}")
        return response
    except Exception as e:
        print(f"Exception when searching companies: {e}")


# Example usage:
token = "your_company_token"
search_companies(token, "ACME")
```

{% endtab %}
{% endtabs %}


# Invite Company

Invite a company to join the TNID ecosystem with createB2bInvite: pre-fill its profile, name at least one representative, and propose a connection type.

Invite a company to join the TNID ecosystem.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

* Include as much data as possible in the invitation so the invitee only has to verify and confirm (they can still change it).
* At least one representative must be included so that, after they register, they can take over management of the newly created company.
* The connection request must be confirmed/accepted by the newly created company's legal representative.
* Most properties are flagged private so the newly created company's data is not exposed.

```graphql
mutation (
  $company: CompanyInput!
  $representatives: [InviteUserInput!]!
  $connectionType: B2bConnectionType!
) {
  createB2bInvite (
    company: $company
    representatives: $representatives
    connectionType: $connectionType
  ) {
    id
    status
    type
    insertedAt
    respondedAt
    updatedAt
    company {
      id
    }
    user {
      id
    }
    invitedCompany {
      id
      legalName
      brandName
      taxId
    }
  }
}
```

Example variables:

```json
{
  "company": {
    "legalName": "Acme Metals",
    "brandName": "Acme",
    "aboutUs": "Sheet metal fabrication",
    "yearFounded": 1998
  },
  "representatives": [
    {
      "email": "jane.smith@acme-metals.example",
      "firstName": "Jane",
      "lastName": "Smith",
      "telephoneNumber": "15555550100",
      "sendInviteEmail": true
    }
  ],
  "connectionType": "PARTNER"
}
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def invite_company(bearer_token, company_to_invite, company_representatives, connection_type):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/company",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql(
        """
        mutation (
            $company: CompanyInput!
            $representatives: [InviteUserInput!]!
            $connectionType: B2bConnectionType!
        ) {
            createB2bInvite (
                company: $company
                representatives: $representatives
                connectionType: $connectionType
            ) {
                id
                status
                type
                insertedAt
                respondedAt
                updatedAt
                company { id }
                user { id }
                invitedCompany { id legalName brandName taxId }
            }
        }
        """
    )

    params = {"company": company_to_invite, "representatives": company_representatives, "connectionType": connection_type}

    try:
        response = client.execute(query, params)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
company_to_invite = {"legalName": "Acme Metals"}
company_representatives = [{"email": "jane.smith@acme-metals.example", "firstName": "Jane", "lastName": "Smith", "sendInviteEmail": True}]

token = "your_company_token"
invite_company(token, company_to_invite, company_representatives, "PARTNER")
```

{% endtab %}
{% endtabs %}


# List Organization Types

Used to list the different Organization types a Company can have.

Used to list the different Organization types a Company can have.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```graphql
query {
  organizationTypes {
    id
    displayName
  }
}
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def list_organization_types(bearer_token):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/company",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql("query { organizationTypes { id displayName } }")

    try:
        response = client.execute(query)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_company_token"
list_organization_types(token)
```

{% endtab %}
{% endtabs %}


# List Vertical Types

Used to list the different vertical types that a Company can have.

Used to list the different vertical types that a Company can have.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```graphql
query {
  verticalTypes {
    id
    displayName
    description
  }
}
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def list_vertical_types(bearer_token):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/company",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql("query { verticalTypes { id displayName description } }")

    try:
        response = client.execute(query)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_company_token"
list_vertical_types(token)
```

{% endtab %}
{% endtabs %}


# Update Company Profile

Use this endpoint to update your company profile fields, subscription topics and subscription settings (updateCompany on /company).

Use this endpoint to update your company profile fields.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```graphql
 mutation (
    $legalName: String
    $brandName: String
    $profileName: String
    $taxId: String
    $yearFounded: Int
    $aboutUs: String
    $metadata: JSON
    $subscriptionTopics: [B2cSubscriptionTopicInput!]
    $subscriptionSettings: CompanySubscriptionSettingsInput
  ) {
    updateCompany (
      legalName: $legalName
      brandName: $brandName
      profileName: $profileName
      taxId: $taxId
      yearFounded: $yearFounded
      aboutUs: $aboutUs
      metadata: $metadata
      subscriptionTopics: $subscriptionTopics
      subscriptionSettings: $subscriptionSettings
    ) {
      id
      legalName
      brandName
      profileName
      taxId
      yearFounded
      aboutUs
      metadata
      subscriptionTopics {
        id
        name
        enable_email
        enable_sms
        enable_voice
      }
      subscriptionSettings {
        enable_email
        enable_sms
        enable_voice
      }
    }
  }
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def update_company_profile(bearer_token, legal_name=None, brand_name=None, profile_name=None, tax_id=None, year_founded=None, about_us=None, metadata=None):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/company",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql(
        """
        mutation (
            $legalName: String
            $brandName: String
            $profileName: String
            $taxId: String
            $yearFounded: Int
            $aboutUs: String
            $metadata: JSON
        ) {
            updateCompany (
                legalName: $legalName
                brandName: $brandName
                profileName: $profileName
                taxId: $taxId
                yearFounded: $yearFounded
                aboutUs: $aboutUs
                metadata: $metadata
            ) {
                id
                legalName
                brandName
                profileName
                taxId
                yearFounded
                aboutUs
                metadata
            }
        }
        """
    )

    params = {"legalName": legal_name, "brandName": brand_name, "profileName": profile_name, "taxId": tax_id,
              "yearFounded": year_founded, "aboutUs": about_us, "metadata": metadata}

    try:
        response = client.execute(query, params)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_company_token"
update_company_profile(token,
                       "Company new legal name",
                       "Company new brand name",
                       "Company new profile name",
                       "44-555555",
                       2003,
                       "New about us",
                       {"companyNumEmployees": "5"})
```

{% endtab %}
{% endtabs %}


# B2B Features

Connect your company with other companies: send, list, respond to and revoke B2B connection requests, and remove connections.


# Send B2B Connection Request

By providing the invited company ID and the connection type, a connection request will be sent to the invited company.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
mutation (
	$invitedCompanyId: ID!
	$connectionType: B2bConnectionType!
          ) {
	createB2bConnectionRequest (
  	invitedCompanyId: $invitedCompanyId
  	connectionType: $connectionType
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedCompany {
    	id
      }
    }
  }

```


# List B2B Connections

Call returns list of connected companies and the relationship type(s) (client, vendor, partner, other).

Call returns list of connected companies and the relationship type(s) (client, vendor, partner, other). For large lists use the cursor-paginated `paginatedB2bConnections` query (see [Pagination](broken://pages/pagination)).

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```graphql
query (
	$includedType: B2bConnectionType
	$excludedType: B2bConnectionType
	$limit: Int
  ) {
	b2bConnections (
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	type
  	createdAt
  	updatedAt
  	startedAt
  	company {
    	id
  	}
  	connectedCompany {
    	id
  	}
	}
  }
```

## Code Examples

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def list_b2b_connections(bearer_token, include_type=None, exclude_type=None, limit_count=10):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/company",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql(
        """
        query (
            $includedType: B2bConnectionType
            $excludedType: B2bConnectionType
            $limit: Int
        ) {
            b2bConnections (
                includedType: $includedType
                excludedType: $excludedType
                limit: $limit
            ) {
                id
                type
                createdAt
                updatedAt
                startedAt
                company { id }
                connectedCompany { id }
            }
        }
        """
    )

    params = {"includedType": include_type, "excludedType": exclude_type, "limit": limit_count}

    try:
        response = client.execute(query, params)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_company_token"
list_b2b_connections(token)
```

{% endtab %}
{% endtabs %}


# List Pending B2B Connection Requests

Returns a list of pending Company connection requests to your Company.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

* Returns a list of pending connection requests this company sent to other companies
* Includes company data, invitation status, and proposed connection type (vendor, client, partner..)

```
query (
	$invitedCompanyId: ID
	$includedType: B2bConnectionType
	$excludedType: B2bConnectionType
	$limit: Int
  ) {
	pendingB2bConnectionRequests (
  	invitedCompanyId: $invitedCompanyId
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
	}
  }

```


# Remove B2B Connection

Use the below mutation to remove existing B2B Connection(s).

```
mutation (
    $connectionId: ID!
  ) {
    removeB2bConnection (
      connectionId: $connectionId
    ) {
      id
      type
      createdAt
      updatedAt
      startedAt
      company {
        id
      }
      connectedCompany {
        id
      }
    }
  }

```


# Cancel B2B Connection Request

Cancels a B2B connection request you've sent.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

If a company wants to revoke a connection request it can choose from the list of pending requests and revoke it.

```
mutation (
	$requestId: ID!
  ) {
	revokeB2bConnectionRequest (
  	requestId: $requestId
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
	}
  }

```


# Get Received B2B Connection Requests

List Company connection requests that you have received and should (probably) respond to.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
query (
	$invitingCompanyId: ID
	$includedType: B2bConnectionType
	$excludedType: B2bConnectionType
	$limit: Int
  ) {
	receivedB2bConnectionRequests (
  	invitingCompanyId: $invitingCompanyId
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
	}
  }

```


# Respond to B2B Connection Requests

Provides functionality to respond to a Company connection request.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
mutation (
	$requestId: ID!
	$response: ConnectionRequestResponse!
  ) {
	respondB2bConnectionRequest (
  	requestId: $requestId
  	response: $response
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
	}
  }

```


# B2C Features

Connect with people: connection and subscription requests, subscribers, opt-outs, SMS opt-in QR codes and people search on the /company server.


# List B2C (People) Connections

Returns list of B2C (People) connections with information about the connection, the Person/User and the company.

```
query (
	$includedType: B2cConnectionType
	$excludedType: B2cConnectionType
	$limit: Int
  ) {
	b2cConnections (
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	type
  	insertedAt
  	updatedAt
  	startedAt
  	company {
    	id
  	}
  	connectedUser {
    	id
      }
    }
  }

```


# List Pending People Connection Requests

Returns a list of B2C connection requests that have not been responded to, with the proposed relationship type (e.g. owner, admin, customer, partner….)

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
query (
	$invitedUserId: ID
	$includedType: B2cConnectionType
	$excludedType: B2cConnectionType
	$limit: Int
  ) {
	pendingB2cConnectionRequests (
  	invitedUserId: $invitedUserId
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# List Active Subscribers

Returns a list of B2C subscriptions for the company including the subscription channel, and all the relevant details.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```json
query (
	$userId: ID
	$includedTopic: B2cSubscriptionTopic
	$excludedTopic: B2cSubscriptionTopic
	$expirationDateBefore: Date
	$expirationDateAfter: Date
	$emailChannelEnabled: Boolean
	$smsChannelEnabled: Boolean
	$voiceChannelEnabled: Boolean
	$limit: Int
  ) {
	b2cSubscriptions (
  	userId: $userId
  	includedTopic: $includedTopic
  	excludedTopic: $excludedTopic
  	expirationDateBefore: $expirationDateBefore
  	expirationDateAfter: $expirationDateAfter
  	emailChannelEnabled: $emailChannelEnabled
  	smsChannelEnabled: $smsChannelEnabled
  	voiceChannelEnabled: $voiceChannelEnabled
  	limit: $limit
	) {
  	id
  	subscriptionTopics
  	shareInformationWithTheCompany
  	stopAllCommunications
  	expirationDate
  	notificationStartTime
  	notificationEndTime
  	timezone
  	insertedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	email {
    	email
  	}
  	smsTelephoneNumber {
    	number
  	}
  	voiceTelephoneNumber {
    	number
  	}
	}
  }

```


# List Pending Subscription Requests

Returns the list of pending B2C subscription requests your company has sent (pendingB2cSubscriptionRequests on /company).

Returns the list of pending B2C subscription requests your company has sent to users. Only requests with status `PENDING` are returned; accepted, rejected or revoked requests are not included.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```graphql
query {
  pendingB2cSubscriptionRequests {
    id
    status
    type
    insertedAt
    respondedAt
    updatedAt
    company {
      id
    }
    user {
      id
    }
    invitedUser {
      id
    }
  }
}
```

Example response:

```json
{
  "data": {
    "pendingB2cSubscriptionRequests": [
      {
        "company": { "id": "00000000-0000-4000-8000-000000000001" },
        "id": "00000000-0000-4000-8000-000000000002",
        "insertedAt": "2026-01-15T09:00:00",
        "invitedUser": { "id": "00000000-0000-4000-8000-000000000003" },
        "respondedAt": null,
        "status": "PENDING",
        "type": "EMAIL",
        "updatedAt": "2026-01-15T09:00:00",
        "user": { "id": "00000000-0000-4000-8000-000000000004" }
      }
    ]
  }
}
```

For large lists use the cursor-paginated `paginatedPendingB2cSubscriptionRequests` query (see [Pagination](broken://pages/pagination)).


# Send Person (B2C) Connection Request

If you find people using search, they can send them a connection request.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
mutation (
	$invitedUserId: ID!
	$connectionType: B2cConnectionType!
  ) {
	createB2cConnectionRequest (
  	invitedUserId: $invitedUserId
  	connectionType: $connectionType
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Invite People (as Company)

Invite a person to Connect with your company.

* Provide as much information as possible and create shallow (unclaimed) profiles
* Send the invites to those people and connection requests

```
mutation (
	$user: InviteUserInput!
	$connectionType: B2cConnectionType!
        ) {
	createB2cInvite (
  	user: $user
  	connectionType: $connectionType
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
    	firstName
    	lastName
  	}
	}
  }

```


# Send Person (B2C) Subscription Request

When a user is found, a Company can send them a Subscription request.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
mutation (
	$invitedUserId: ID!
	$requestEmailSubscription: Boolean
	$requestSmsSubscription: Boolean
	$requestVoiceSubscription: Boolean
  ) {
	createB2cSubscriptionRequest (
  	invitedUserId: $invitedUserId
  	requestEmailSubscription: $requestEmailSubscription
  	requestSmsSubscription: $requestSmsSubscription
  	requestVoiceSubscription: $requestVoiceSubscription
	) {
  	id
  	status
  	email_requested
  	sms_requested
  	voice_requested
  	insertedAt
  	updatedAt
  	respondedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Remove B2C Connection

Use the below mutation to remove existing B2C connection(s).

```
mutation (
    $connectionId: ID!
  ) {
    removeB2cConnection (
      connectionId: $connectionId
    ) {
      id
      type
      insertedAt
      updatedAt
      startedAt
      company {
        id
      }
      connectedUser {
        id
      }
    }
  }
```


# Cancel Pending B2C Connection Request

Find a pending Connection request on the list and revoke it.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
mutation (
	$requestId: ID!
  ) {
	revokeB2cConnectionRequest (
  	requestId: $requestId
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Cancel Pending B2C Subscription Request

Find a pending Subscription request, and revoke it.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
mutation (
	$requestId: ID!
  ) {
	revokeB2cSubscriptionRequest (
  	requestId: $requestId
	) {
  	id
  	status
  	email_requested
  	sms_requested
  	voice_requested
  	insertedAt
  	updatedAt
  	respondedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Get Pending Connection Requests (People)

List pending requests made by People to connect to your Company.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
query (
	$userId: ID
	$includedType: B2cConnectionType
	$excludedType: B2cConnectionType
	$limit: Int
  ) {
	pendingC2bConnectionRequests (
  	userId: $userId
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
  	respondedByUser {
    	id
  	}
	}
  }

```


# Respond to C2B Connection Request

Respond to a Connection request received from a Person.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
mutation (
	$requestId: ID!
	$response: ConnectionRequestResponse!
  ) {
	respondC2bConnectionRequest (
  	requestId: $requestId
  	response: $response
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
  	respondedByUser {
    	id
  	}
	}
  }

```


# Get Connected People

Returns list of Business to Consumer (B2C) Connections with information about the connection, the user and the company

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

```
query (
	$includedType: B2cConnectionType
	$excludedType: B2cConnectionType
	$limit: Int
  ) {
	b2cConnections (
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	type
  	insertedAt
  	updatedAt
  	startedAt
  	company {
    	id
  	}
  	connectedUser {
    	id
  	}
	}
  }

```


# Generate SMS Opt-In QR Code

Use this mutation to generate an SMS opt-in QR code.

```
mutation (
      $telephoneNumberId: ID!
      $subscriptionTopicIds: [ID!]!
      $type: SmsOptInQrCodeType!
  ) {
    generateSmsOptInQrCode (
      telephoneNumberId: $telephoneNumberId
      subscriptionTopicIds: $subscriptionTopicIds
      type: $type
    ) {
      message
      qrCode
      telephoneNumber {
        id
      }
    }
  }
```


# Search People

Use this function to search for Users (People) within the TNID platform.

{% hint style="info" %}
The company GraphQL API is accessible at /company
{% endhint %}

* Search for users who have existing profiles
* Name field queries full name or username fields

```
query (
	$name: String
	$email: String
	$telephoneNumber: String
	$limit: Int
  ) {
	users (
  	name: $name
  	email: $email
  	telephoneNumber: $telephoneNumber
  	limit: $limit
	) {
  	id
  	firstName
  	lastName
  	middleName
  	username
	}
  }

```


# List Opt-Out Requests

Returns a list of Opt-out requests with information about the request, the user and the company.

```

  query (
	$type: OptOutRequestType
	$status: OptOutRequestStatus
	$source: String
	$destination: String
	$externalId: String
	$timestampBefore: NaiveDateTime
	$timestampAfter: NaiveDateTime
	$limit: Int
  ) {
	optOutRequests (
  	type: $type
  	status: $status
  	source: $source
  	destination: $destination
  	externalId: $externalId
  	timestampBefore: $timestampBefore
  	timestampAfter: $timestampAfter
  	limit: $limit
	) {
  	id
  	source
  	destination
  	externalId
  	messageBody
  	type
  	status
  	createdAt
  	updatedAt
  	timestamp
  	metadata
  	company {
    	id
    	legalName
    	brandName
    	profileName
    	taxId
    	yearFounded
    	aboutUs
    	metadata
    	verified
    	logoUrl
    	verticalType {
      	id
      	displayName
      	description
    	}
    	organizationType {
      	id
      	displayName
    	}
    	addresses {
      	city
      	country
      	state
      	street
      	types
      	zipCode
    	}
    	emails {
      	email
    	}
    	telephoneNumbers {
      	number
    	}
    	socialNetworks {
      	type
      	url
    	}
    	webpages {
      	type
      	url
    	}
    	specialties {
      	description
      	title
    	}
    	subscriptionTopics {
      	id
      	name
    	}
  	}
  	user {
    	id
    	username
    	firstName
    	middleName
    	lastName
    	birthdate
    	aboutMe
    	timezone
    	metadata
    	addresses {
      	city
      	country
      	state
      	street
      	types
      	zipCode
    	}
    	emails {
      	email
    	}
    	telephoneNumbers {
      	number
    	}
    	socialNetworks {
      	type
      	url
    	}
    	webpages {
      	type
      	url
    	}
    	interests {
      	description
      	title
    	}
  	}
	}
  }

```


# Create Opt-out Request

Creates and then returns an opt-out request.

```
mutation (
    $source: String!
    $destination: String!
    $externalId: String
    $messageBody: String
    $type: OptOutRequestType!
    $timestamp: NaiveDateTime!
    $metadata: JSON
  ) {
    createOptOutRequest (
      source: $source
      destination: $destination
      externalId: $externalId
      messageBody: $messageBody
      type: $type
      timestamp: $timestamp
      metadata: $metadata
    ) {
      id
      source
      destination
      externalId
      messageBody
      type
      status
      createdAt
      updatedAt
      timestamp
      metadata
      company {
        id
      }
    }
  }

```


# Upsert B2C Subscription

Create or update a B2C subscription.

```
mutation (
    $companyId: ID!
    $type: B2cSubscriptionType!
    $emailId: ID
    $telephoneNumberId: ID
    $subscriptionTopicIds: [ID!]!
    $expirationDate: Date
    $notificationStartTime: Time
    $notificationEndTime: Time
  ) {
    upsertB2cSubscription (
      companyId: $companyId
      type: $type
      emailId: $emailId
      telephoneNumberId: $telephoneNumberId
      subscriptionTopicIds: $subscriptionTopicIds
      expirationDate: $expirationDate
      notificationStartTime: $notificationStartTime
      notificationEndTime: $notificationEndTime
      ) {
        ... on B2cEmailSubscription {
          id
          insertedAt
          updatedAt
          type
          subscriptionTopics
          expirationDate
          notificationStartTime
          notificationEndTime
          email
          company {
            id
          }
          user {
            id
          }
        }
        ... on B2cSmsSubscription {
          id
          insertedAt
          updatedAt
          type
          subscriptionTopics
          expirationDate
          notificationStartTime
          notificationEndTime
          telephoneNumber
          company {
            id
          }
          user {
            id
          }
        }
        ... on B2cVoiceSubscription {
          id
          insertedAt
          updatedAt
          type
          subscriptionTopics
          expirationDate
          notificationStartTime
          notificationEndTime
          telephoneNumber
          company {
            id
          }
          user {
            id
          }
        }

```


# Delete B2C Subscription

removeB2cSubscription on /company: remove a person's email, SMS or voice subscription to your company.

```
mutation (
    $subscriptionId: ID!
  ) {
    removeB2cSubscription (
      subscriptionId: $subscriptionId
    ) {
      ... on B2cEmailSubscription {
        id
        insertedAt
        updatedAt
        type
        subscriptionTopics
        expirationDate
        notificationStartTime
        notificationEndTime
        email
        company {
          id
        }
        user {
          id
        }
      }
      ... on B2cSmsSubscription {
        id
        insertedAt
        updatedAt
        type
        subscriptionTopics
        expirationDate
        notificationStartTime
        notificationEndTime
        telephoneNumber
        company {
          id
        }
        user {
          id
        }
      }
      ... on B2cVoiceSubscription {
        id
        insertedAt
        updatedAt
        type
        subscriptionTopics
        expirationDate
        notificationStartTime
        notificationEndTime
        telephoneNumber
        company {
          id
        }
        user {
          id
        }
      }
    }
  }

```


# Webhooks

Register a company webhook so TNID calls your endpoint when connection requests, subscriptions, opt-outs, forms and social-network verifications change.

Instead of polling `received…Requests` and `…Connections`, register a webhook and let TNID call you. Webhooks belong to a company and are managed on the `/company` GraphQL server.

## Register a webhook

```graphql
mutation (
  $url: String!
  $name: String
  $bearerToken: String
  $customHeaders: JSON
  $receiveSubscriptionEvents: Boolean
  $receiveOptOutEvents: Boolean
  $b2bConnectionRequestReceivedWebhook: Boolean
  $b2bConnectionRequestResponseWebhook: Boolean
  $b2cConnectionRequestResponseWebhook: Boolean
  $c2bConnectionRequestReceivedWebhook: Boolean
  $b2cSubscriptionRequestResponseWebhook: Boolean
  $companySocialNetworkStatusChangeWebhook: Boolean
  $b2bFormInstanceSubmissionRequestReceivedWebhook: Boolean
) {
  createCompanyWebhook (
    url: $url
    name: $name
    bearerToken: $bearerToken
    customHeaders: $customHeaders
    receiveSubscriptionEvents: $receiveSubscriptionEvents
    receiveOptOutEvents: $receiveOptOutEvents
    b2bConnectionRequestReceivedWebhook: $b2bConnectionRequestReceivedWebhook
    b2bConnectionRequestResponseWebhook: $b2bConnectionRequestResponseWebhook
    b2cConnectionRequestResponseWebhook: $b2cConnectionRequestResponseWebhook
    c2bConnectionRequestReceivedWebhook: $c2bConnectionRequestReceivedWebhook
    b2cSubscriptionRequestResponseWebhook: $b2cSubscriptionRequestResponseWebhook
    companySocialNetworkStatusChangeWebhook: $companySocialNetworkStatusChangeWebhook
    b2bFormInstanceSubmissionRequestReceivedWebhook: $b2bFormInstanceSubmissionRequestReceivedWebhook
  ) {
    id
    url
    name
  }
}
```

```json
{
  "url": "https://api.example.com/tnid/webhook",
  "name": "production",
  "bearerToken": "<a secret you generate>",
  "customHeaders": { "X-Environment": "prod" },
  "receiveSubscriptionEvents": true,
  "receiveOptOutEvents": true,
  "b2bConnectionRequestReceivedWebhook": true,
  "b2bConnectionRequestResponseWebhook": true,
  "b2cConnectionRequestResponseWebhook": true,
  "c2bConnectionRequestReceivedWebhook": true,
  "b2cSubscriptionRequestResponseWebhook": true,
  "companySocialNetworkStatusChangeWebhook": false,
  "b2bFormInstanceSubmissionRequestReceivedWebhook": false
}
```

* `url` must be HTTPS and reachable from the internet.
* `bearerToken` is sent back to you on every delivery as `Authorization: Bearer <bearerToken>`. Generate a random secret and verify it on every request; reject calls that do not carry it.
* `customHeaders` are added verbatim to every delivery.
* Each boolean subscribes the webhook to one event type. Omitted flags default to off.

## Event types

| Flag                                              | Fires when                                                                                       |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `receiveSubscriptionEvents`                       | A person subscribes to your company, changes their topics or channels, or removes a subscription |
| `receiveOptOutEvents`                             | An opt-out is recorded for your company                                                          |
| `b2bConnectionRequestReceivedWebhook`             | Another company sends you a B2B connection request                                               |
| `b2bConnectionRequestResponseWebhook`             | A company accepts or rejects your B2B connection request                                         |
| `b2cConnectionRequestResponseWebhook`             | A person accepts or rejects your B2C connection request                                          |
| `c2bConnectionRequestReceivedWebhook`             | A person sends you a connection request                                                          |
| `b2cSubscriptionRequestResponseWebhook`           | A person accepts or rejects your subscription request                                            |
| `companySocialNetworkStatusChangeWebhook`         | A social-network link on your profile changes verification status                                |
| `b2bFormInstanceSubmissionRequestReceivedWebhook` | Another company asks you to fill in one of its forms                                             |

Sentiment analysis also delivers to your webhooks. When `reportExternalCommunication` detects an unsubscribe intent, TNID posts:

```json
{
  "external_communication_id": "00000000-0000-4000-8000-000000000001",
  "timestamp": "2026-01-15T09:00:00.000000",
  "action": "unsubscribe"
}
```

Confirm or reject it with `feedbackOnDetectedUnsubscribe(externalCommunicationId, action: UNSUBSCRIBE | DONT_UNSUBSCRIBE)`. See [sentiment analysis](https://docs.tnid.com/api-reference/002_company/003_message_sentiment_analysis) in the API Reference.

{% hint style="warning" %}
Payload schemas for the connection, subscription and opt-out events are not yet published on this site. Register a webhook on staging, trigger the event, and capture a sample; the fields mirror the objects returned by the corresponding `received…Requests`, `…Connections` and `optOutRequests` queries. Contact <support@tsgglobal.com> if you need the schemas in advance.
{% endhint %}

## Respond, retries and ordering

Return a `2xx` status quickly and do the work asynchronously. Treat deliveries as at-least-once: key your handler on the object `id` in the payload so a repeated delivery is harmless. Use `insertedAt`/`updatedAt` on the referenced object, not arrival order, when ordering matters.

## Manage webhooks

* `companyWebhooks` lists your webhooks with their flags.
* `updateCompanyWebhook(id, …)` changes only the arguments you pass; omitted settings keep their values.
* `removeCompanyWebhook(id)` stops delivery immediately.

All four operations, with full examples, are in the [API Reference](https://docs.tnid.com/api-reference/002_company/014_webhooks).


# Update User profile

Use this feature to update User profile fields such as username, name, birthdate, about-me text and metadata.

Use this feature to update User profile fields.

{% hint style="info" %}
The User GraphQL API is accessible at /user
{% endhint %}

```graphql
 mutation (
	$username: String
	$firstName: String
	$lastName: String
	$middleName: String
	$birthdate: Date
	$aboutMe: String
	$metadata: JSON
  ) {
	updateUser (
  	username: $username
  	firstName: $firstName
  	lastName: $lastName
  	middleName: $middleName
  	birthdate: $birthdate
  	aboutMe: $aboutMe
  	metadata: $metadata
	) {
  	id
  	username
  	firstName
  	lastName
  	middleName
  	birthdate
  	aboutMe
  	metadata
	}
  }
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def user_update_profile(bearer_token, username=None, first_name=None, last_name=None, middle_name=None, birthdate=None, about_me=None, metadata=None):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/user",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql(
        """
        mutation (
            $username: String
            $firstName: String
            $lastName: String
            $middleName: String
            $birthdate: Date
            $aboutMe: String
            $metadata: JSON
        ) {
            updateUser (
                username: $username
                firstName: $firstName
                lastName: $lastName
                middleName: $middleName
                birthdate: $birthdate
                aboutMe: $aboutMe
                metadata: $metadata
            ) {
                id
                username
                firstName
                lastName
                middleName
                birthdate
                aboutMe
                metadata
            }
        }
        """
    )

    params = {"username": username, "firstName": first_name, "lastName": last_name,
              "middleName": middle_name, "birthdate": birthdate, "aboutMe": about_me, "metadata": metadata}

    try:
        response = client.execute(query, params)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_user_token"
user_update_profile(token, "username", "John", "Smith")
```

{% endtab %}
{% endtabs %}


# Search User (People)

Search for users who have existing profiles. Name field queries full name or username fields.

```
query (
	$name: String
	$email: String
	$telephoneNumber: String
	$limit: Int
  ) {
	users (
  	name: $name
  	email: $email
  	telephoneNumber: $telephoneNumber
  	limit: $limit
	) {
  	id
  	username
  	firstName
  	middleName
  	lastName
  	birthdate
  	aboutMe
  	timezone
  	metadata
  	addresses {
    	city
    	country
    	state
    	street
    	types
    	zipCode
  	}
  	emails {
    	email
  	}
  	telephoneNumbers {
    	number
  	}
  	socialNetworks {
    	type
    	url
  	}
  	webpages {
    	type
    	url
  	}
  	interests {
    	description
    	title
  	}
	}
  }

```


# Invite User (People)

Provide as much information as possible and create shallow profiles. Send the invites to those people and connection requests.

```
mutation (
	$user: InviteUserInput!
	$connectionType: C2cConnectionType!
  ) {
	createC2cInvite (
  	user: $user
  	connectionType: $connectionType
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedUser {
    	id
    	username
  	}
	}
  }

```


# Connections

As a user, connect with companies: send C2B requests, respond to company requests, list and revoke.


# Send C2B Connection Request

As a user, send a connection request to a company you found with search (createC2bConnectionRequest on /user).

If you find a company using search, you can send it a connection request.

{% hint style="info" %}
The User GraphQL API is accessible at /user
{% endhint %}

```graphql
mutation (
	$invitedCompanyId: ID!
	$connectionType: B2cConnectionType!
  ) {
	createC2bConnectionRequest (
  	invitedCompanyId: $invitedCompanyId
  	connectionType: $connectionType
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
  	respondedByUser {
    	id
  	}
	}
  }
```


# List B2C connections

b2cConnections on /user: list the companies you are connected to and the connection type.

```
query (
	$includedType: B2cConnectionType
	$excludedType: B2cConnectionType
	$limit: Int
  ) {
	b2cConnections (
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	type
  	insertedAt
  	updatedAt
  	startedAt
  	company {
    	id
  	}
  	connectedUser {
    	id
  	}
	}
  }

```


# List received pending B2C Connection Requests

List received pending requests that the user needs to respond to.

```
query (
	$includedType: B2cConnectionType
	$excludedType: B2cConnectionType
	$limit: Int
  ) {
	receivedB2cConnectionRequests (
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	company {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Respond to received B2C Connection Request

Accept or reject a received B2C connection request.

```
mutation (
	$requestId: ID!
	$response: ConnectionRequestResponse!
  ) {
	respondB2cConnectionRequest (
  	requestId: $requestId
  	response: $response
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	company {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Revoke sent C2B Connection Request

revokeC2bConnectionRequest on /user: withdraw a pending connection request you sent to a company.

```
mutation (
	$requestId: ID!
  ) {
	revokeC2bConnectionRequest (
  	requestId: $requestId
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	invitedCompany {
    	id
  	}
  	user {
    	id
  	}
  	respondedByUser {
    	id
  	}
	}
  }

```


# List sent pending C2B Connection Requests

List sent pending requests that the user sent.

```
query (
	$includedType: B2cConnectionType
	$excludedType: B2cConnectionType
	$limit: Int
  ) {
	pendingC2bConnectionRequests (
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedCompany {
    	id
  	}
  	respondedByUser {
    	id
  	}
	}
  }

```


# Spam Reporting Features

Let users report unwanted SMS, MMS or calls and list their reports on the /user server.


# Create Spam Report

Use this endpoint to create a spam report for an unwanted SMS, MMS or call (createSpamReport on /user).

Use this endpoint to create a spam report.

{% hint style="info" %}
The User GraphQL API is accessible at /user
{% endhint %}

```graphql
  mutation (
	$fromNumber: String!
	$toNumber: String!
	$channelType: SpamReportChannelType!
	$timestamp: NaiveDateTime!
	$issueType: SpamReportIssueType
	$userNote: String
	$messageContent: String
	$metadata: JSON
  ) {
	createSpamReport (
  	fromNumber: $fromNumber
  	toNumber: $toNumber
  	channelType: $channelType
  	timestamp: $timestamp
  	issueType: $issueType
  	userNote: $userNote
  	messageContent: $messageContent
  	metadata: $metadata
	) {
  	id
  	fromNumber
  	toNumber
  	userNote
  	messageContent
  	channelType
  	issueType
  	status
  	createdAt
  	updatedAt
  	timestamp
  	metadata
  	user {
    	id
  	}
	}
  }
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def user_create_spam_report(bearer_token, from_number, to_number, channel_type, timestamp, issue_type=None, user_note=None, message_content=None, metadata=None):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/user",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql(
        """
        mutation (
            $fromNumber: String!
            $toNumber: String!
            $channelType: SpamReportChannelType!
            $timestamp: NaiveDateTime!
            $issueType: SpamReportIssueType
            $userNote: String
            $messageContent: String
            $metadata: JSON
        ) {
            createSpamReport (
                fromNumber: $fromNumber
                toNumber: $toNumber
                channelType: $channelType
                timestamp: $timestamp
                issueType: $issueType
                userNote: $userNote
                messageContent: $messageContent
                metadata: $metadata
            ) {
                id
                fromNumber
                toNumber
                userNote
                messageContent
                channelType
                issueType
                status
                createdAt
                updatedAt
                timestamp
                metadata
                user {
                    id
                }
            }
        }
        """
    )

    params = {"fromNumber": from_number, "toNumber": to_number, "channelType": channel_type,
              "timestamp": timestamp, "issueType": issue_type, "userNote": user_note,
              "messageContent": message_content, "metadata": metadata}

    try:
        response = client.execute(query, params)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_user_token"
user_create_spam_report(token,
                        "15555555555",
                        "16666666666",
                        "MMS",
                        "2023-10-31 11:30:00",
                        "SPAM",
                        "Note about the report",
                        "Original message content",
                        {"customField": "custom value"})
```

{% endtab %}
{% endtabs %}


# List Spam Reports

Used to list Spam Reports with optional filters by channel, issue type and status (spamReports on /user).

Used to list Spam Reports with optional filters. For large result sets use the cursor-paginated twin, `paginatedSpamReports` (see [Pagination](broken://pages/pagination)).

{% hint style="info" %}
The User GraphQL API is accessible at /user
{% endhint %}

```graphql
query (
	$channelType: SpamReportChannelType
	$issueType: SpamReportIssueType
	$includedStatus: SpamReportStatus
	$excludedStatus: SpamReportStatus
	$limit: Int
  ) {
	spamReports (
  	channelType: $channelType
  	issueType: $issueType
  	includedStatus: $includedStatus
  	excludedStatus: $excludedStatus
  	limit: $limit
	) {
  	id
  	fromNumber
  	toNumber
  	userNote
  	messageContent
  	channelType
  	issueType
  	status
  	createdAt
  	updatedAt
  	timestamp
  	metadata
  	user {
    	id
  	}
	}
  }
```

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

# Please note that this basic example won't work if you have an asyncio event loop running.
# In some python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you.
# In that case you should use instead https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage
def user_list_spam_reports(bearer_token, channel_type=None, issue_type=None, included_status=None, excluded_status=None, limit_count=10):
    transport = AIOHTTPTransport(
        url="https://api.staging.v2.tnid.com/user",
        headers={"Authorization": f"Bearer {bearer_token}"}
    )
    client = Client(transport=transport, fetch_schema_from_transport=True)

    query = gql(
        """
        query (
            $channelType: SpamReportChannelType
            $issueType: SpamReportIssueType
            $includedStatus: SpamReportStatus
            $excludedStatus: SpamReportStatus
            $limit: Int
        ) {
            spamReports (
                channelType: $channelType
                issueType: $issueType
                includedStatus: $includedStatus
                excludedStatus: $excludedStatus
                limit: $limit
            ) {
                id
                fromNumber
                toNumber
                userNote
                messageContent
                channelType
                issueType
                status
                createdAt
                updatedAt
                timestamp
                metadata
                user {
                    id
                }
            }
        }
        """
    )

    params = {"channelType": channel_type, "issueType": issue_type, "includedStatus": included_status,
              "excludedStatus": excluded_status, "limit": limit_count}

    try:
        response = client.execute(query, params)
        print(f"Response OK: {response}")
        return response
    except Exception as e:
        print(f"Exception: {e}")


# Example usage:
token = "your_user_token"
user_list_spam_reports(token, "MMS")
```

{% endtab %}
{% endtabs %}


# Company Features

What a user can do with companies and other people from the /user server: create a company, mint client secrets, and manage C2C connections and subscriptions.


# Search Companies

companies query on /user: search companies by name, tax ID, email, telephone number or webpage.

This will return a list of companies matching the query and in case queried props are visible to the querier, it will be possible to query by:

* ID
* Legal name (with name field)
* Brand name (with name field)
* Profile name (with name field)
* TaxID
* Telephone number
* Email
* Webpage (social media or other)

Each result will return a list of companies. It is possible to pass a combination of queryable props.

```
query (
    $id: ID
    $name: String
    $taxId: String
    $email: String
    $telephoneNumber: String
    $webpage: String
    $limit: Int
    $metadata: JSON
  ) {
    companies (
      id: $id
      name: $name
      taxId: $taxId
      email: $email
      telephoneNumber: $telephoneNumber
      webpage: $webpage
      limit: $limit
      metadata: $metadata
    ) {
      id
      legalName
      brandName
      profileName
      taxId
      yearFounded
      aboutUs
      metadata
      verified
      logoUrl
      verticalType {
        id
        displayName
        description
      }
      organizationType {
        id
        displayName
      }
      addresses {
        city
        country
        state
        street
        types
        zipCode
      }
      emails {
        email
      }
      telephoneNumbers {
        number
      }
      socialNetworks {
        type
        url
      }
      webpages {
        type
        url
      }
      specialties {
        description
        title
      }
      subscriptionTopics {
        id
        name
      }
      subscriptionSettings {
        enable_email
        enable_sms
        enable_voice
      }
    }
  }

```


# Create company profile

createCompany on /user: register a company with its profile, contacts, visibility settings and, optionally, a client secret for API access.

```
mutation (
    $legalName: String!
    $brandName: String
    $profileName: String!
    $taxId: String
    $yearFounded: Int
    $aboutUs: String
    $metadata: JSON
    $emails: [CompanyEmailInput!]
    $telephoneNumbers: [CompanyTelephoneNumberInput!]
    $addresses: [CompanyAddressInput!]
    $webpages: [WebpageInput!]
    $socialNetworks: [SocialMediaInput!]
    $trustedDomains: [TrustedDomainInput!]
    $subscriptionTopics: [B2cSubscriptionTopicInput!]
    $subscriptionSettings: CompanySubscriptionSettingsInput
    $verticalTypeId: ID
    $organizationTypeId: ID
    $companySpecialties: [ID]
    $legalNameVisibility: [CompanyVisibilityType!]
    $brandNameVisibility: [CompanyVisibilityType!]
    $taxIdVisibility: [CompanyVisibilityType!]
    $aboutUsVisibility: [CompanyVisibilityType!]
    $yearFoundedVisibility: [CompanyVisibilityType!]
    $verticalTypeVisibility: [CompanyVisibilityType!]
    $organizationTypeVisibility: [CompanyVisibilityType!]
    $specialtiesVisibility: [CompanyVisibilityType!]
    $b2cVisibility: [CompanyVisibilityType!]
    $metadataVisibility: [CompanyVisibilityType!]
    $provisionCompanyClientSecret: Boolean
    $connectionType: AdminB2cConnectionType
  ) {
    createCompany (
      legalName: $legalName
      brandName: $brandName
      profileName: $profileName
      taxId: $taxId
      yearFounded: $yearFounded
      aboutUs: $aboutUs
      metadata: $metadata
      emails: $emails
      telephoneNumbers: $telephoneNumbers
      addresses: $addresses
      webpages: $webpages
      socialNetworks: $socialNetworks
      trustedDomains: $trustedDomains
      subscriptionTopics: $subscriptionTopics
      subscriptionSettings: $subscriptionSettings
      verticalTypeId: $verticalTypeId
      organizationTypeId: $organizationTypeId
      companySpecialties: $companySpecialties
      legalNameVisibility: $legalNameVisibility
      brandNameVisibility: $brandNameVisibility
      taxIdVisibility: $taxIdVisibility
      aboutUsVisibility: $aboutUsVisibility
      yearFoundedVisibility: $yearFoundedVisibility
      verticalTypeVisibility: $verticalTypeVisibility
      organizationTypeVisibility: $organizationTypeVisibility
      specialtiesVisibility: $specialtiesVisibility
      b2cVisibility: $b2cVisibility
      metadataVisibility: $metadataVisibility
      provisionCompanyClientSecret: $provisionCompanyClientSecret
      connectionType: $connectionType
    ) {
      id
      clientSecret {
      clientId
      clientSecret
      description
      }
    }
  }

```


# Create Company Client Secret

Create a company client secret using the below mutation.

```
mutation (
    $companyId: ID!
    $description: String
  ) {
    createCompanyClientSecret (
      companyId: $companyId
      description: $description
    ) {
      clientId
      clientSecret
      description
    }
  }

```


# List Pending C2C Connection Requests

Returns a list of pending connection requests this user sent to other users.

```
query (
	$invitedUserId: ID
	$includedType: C2cConnectionType
	$excludedType: C2cConnectionType
	$limit: Int
  ) {
	pendingC2cConnectionRequests (
  	invitedUserId: $invitedUserId
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Remove C2C Connection

Use the below mutation to remove an existing C2C connection.

```
mutation (
    $connectionId: ID!
  ) {
    removeC2cConnection (
      connectionId: $connectionId
    ) {
      id
      type
      insertedAt
      updatedAt
      startedAt
      user {
        id
      }
      connectedUser {
        id
      }
    }
  }

```


# Revoke sent C2C Connection Request

Revoke a C2C connection request you sent.

```
mutation (
	$requestId: ID!
  ) {
	revokeC2cConnectionRequest (
  	requestId: $requestId
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	invitedUser {
    	id
  	}
  	user {
    	id
  	}


	}
  }

```


# List Received C2C Connection Requests

The user can list pending connection requests they need to respond.

```
query (
	$invitingUserId: ID
	$includedType: C2cConnectionType
	$excludedType: C2cConnectionType
	$limit: Int
  ) {
	receivedC2cConnectionRequests (
  	invitingUserId: $invitingUserId
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Send C2C Connection Request

Find people using search, and then send them a connection request

```
mutation (
	$invitedUserId: ID!
	$connectionType: C2cConnectionType!
  ) {
	createC2cConnectionRequest (
  	invitedUserId: $invitedUserId
  	connectionType: $connectionType
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Respond C2C Connection Request

Respond to the connection request received from people.

```
 mutation (
	$requestId: ID!
	$response: ConnectionRequestResponse!
  ) {
	respondC2cConnectionRequest (
  	requestId: $requestId
  	response: $response
	) {
  	id
  	status
  	type
  	insertedAt
  	respondedAt
  	updatedAt
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# List C2C Connections

c2cConnections on /user: list the people you are connected to and the connection type.

Returns list of C2C connections with information about the connection, and the users.

```
query (
	$includedType: C2cConnectionType
	$excludedType: C2cConnectionType
	$limit: Int
  ) {
	c2cConnections (
  	includedType: $includedType
  	excludedType: $excludedType
  	limit: $limit
	) {
  	id
  	type
  	insertedAt
  	updatedAt
  	startedAt
  	user {
    	id
  	}
  	connectedUser {
    	id
  	}
	}
  }

```


# Remove B2C Subscription

removeB2cSubscription on /user: cancel one of your email, SMS or voice subscriptions to a company.

```
mutation (
    $subscriptionId: ID!
  ) {
    removeB2cSubscription (
      subscriptionId: $subscriptionId
    ) {
      ... on B2cEmailSubscription {
        id
        insertedAt
        updatedAt
        type
        subscriptionTopics
        expirationDate
        notificationStartTime
        notificationEndTime
        email
        company {
          id
        }
        user {
          id
        }
      }
      ... on B2cSmsSubscription {
        id
        insertedAt
        updatedAt
        type
        subscriptionTopics
        expirationDate
        notificationStartTime
        notificationEndTime
        telephoneNumber
        company {
          id
        }
        user {
          id
        }
      }
      ... on B2cVoiceSubscription {
        id
        insertedAt
        updatedAt
        type
        subscriptionTopics
        expirationDate
        notificationStartTime
        notificationEndTime
        telephoneNumber
        company {
          id
        }
        user {
          id
        }
      }
    }
  }

```


# Respond to B2C Subscription Request

respondB2cSubscriptionRequest on /user: accept or reject a company's request to subscribe you to its messages.

```
mutation (
    $requestId: ID!
    $response: SubscriptionRequestResponse!
  ) {
    respondB2cSubscriptionRequest (
      requestId: $requestId
      response: $response
    ) {
      id
      status
      type
      insertedAt
      respondedAt
      updatedAt
      user {
        id
      }
      company {
        id
      }
      invitedUser {
        id
      }
    }
  }

```


# Group Features

Create and manage groups: search, membership, invites and join requests on the /user server.


# Search Groups

Search for groups. Each result will return a list of groups.

```
   query (
	$name: String
	$limit: Int
  ) {
	groups (
  	name: $name
  	limit: $limit
	) {
  	id
  	name
  	metadata
  	logo_url
  	visibility
	}
  }

```


# List User's Groups

List the groups the user is a member of with their role.

```
  query (
	$limit: Int
  ) {
	userGroups (
  	limit: $limit
	) {
  	role
  	group {
  	id
  	name
  	metadata
  	logo_url
  	visibility
  	}
	}
  }

```


# Create Group

createGroup on /user: create a group with a name, metadata and visibility.

```
mutation (
	$name: String!
	$metadata: JSON
	$visibility: [GroupVisibilityType!]
  ) {
	createGroup (
  	name: $name
  	metadata: $metadata
  	visibility: $visibility
	) {
  	id
  	}
    }

```


# Update group

updateGroup on /user: change a group's name, metadata or visibility.

```
   mutation (
	$groupId: ID!
	$name: String
	$metadata: JSON
	$visibility: [GroupVisibilityType!]
  ) {
	updateGroup (
  	groupId: $groupId
  	name: $name
  	metadata: $metadata
  	visibility: $visibility
	) {
  	id
  	name
  	metadata
  	visibility
  	}
}

```


# Invite User to Group

Send an invite to a user to a group.

```
 mutation (
	$groupId: ID!
	$invitedUserId: ID!
	$role: GroupRoleType!
  ) {
	createGroupUserMemberInvite (
  	groupId: $groupId
  	invitedUserId: $invitedUserId
  	role: $role
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Respond to Group invite

Respond to a group invite you received.

```
 mutation (
	$inviteId: ID!
	$response: GroupInviteResponse!
  ) {
	respondGroupUserMemberInvite(
  	inviteId: $inviteId
  	response: $response
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# List pending Group invites (Sent)

pendingGroupUserMemberInvites on /user: list the group invites you have sent that are still pending.

```
query (
	$groupId: ID
	$invitedUserId: ID
	$limit: Int
  ) {
	pendingGroupUserMemberInvites (
  	groupId: $groupId
  	invitedUserId: $invitedUserId
  	limit: $limit
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# List pending Group invites (Received)

receivedGroupUserMemberInvites on /user: list the group invites waiting for your response.

```
   query (
	$invitingUserId: ID
	$limit: Int
  ) {
	receivedGroupUserMemberInvites (
  	invitingUserId: $invitingUserId
  	limit: $limit
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Revoke Pending Group User Member Invite

revokeGroupUserMemberInvite on /user: withdraw a group invite you sent.

```
mutation (
	$inviteId: ID!
  ) {
	revokeGroupUserMemberInvite(
  	inviteId: $inviteId
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
  	invitedUser {
    	id
  	}
	}
  }

```


# Send Group user member join request

createGroupUserMemberJoinRequest on /user: ask to join a group.

```
mutation (
	$groupId: ID!
  ) {
	createGroupUserMemberJoinRequest (
  	groupId: $groupId
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
	}
  }

```


# List Received Pending Group user member join request

Group admins can list pending group user member join requests to a group.

```
 query (
	$groupId: ID!
	$limit: Int
  ) {
	receivedGroupUserMemberJoinRequests (
  	groupId: $groupId
  	limit: $limit
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
	}
  }

```


# List Pending Group user member join request

pendingGroupUserMemberJoinRequests on /user: list the join requests you have sent that are still pending.

```
 query (
	$limit: Int
  ) {
	pendingGroupUserMemberJoinRequests (
  	limit: $limit
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
	}
  }

```


# Revoke Group user member join request

revokeGroupUserMemberJoinRequest on /user: withdraw a join request you sent.

```
mutation (
	$joinRequestId: ID!
  ) {
	revokeGroupUserMemberJoinRequest(
  	joinRequestId: $joinRequestId
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
	}
  }

```


# Respond to Group user member join request

Group admin can accept or reject a pending group user member join request.

```
 mutation (
	$joinRequestId: ID!
	$response: GroupInviteResponse!
  ) {
	respondGroupUserMemberJoinRequest(
  	joinRequestId: $joinRequestId
  	response: $response
	) {
  	id
  	status
  	role
  	insertedAt
  	respondedAt
  	updatedAt
  	group {
    	id
  	}
  	user {
    	id
  	}
	}
  }

```


# 10DLC Brands

Register and manage 10DLC brands through TNID: API keys, draft and submit flows, statuses, listing and sharing brands with partner companies.

10DLC is the US carrier registration system for application-to-person messaging on local numbers. A **brand** is the registered business that sends messages; **campaigns** (next page) describe what it sends. TNID submits brands and campaigns to The Campaign Registry (TCR) for you and tracks their status.

{% hint style="info" %}
All 10DLC operations are on the `/company` GraphQL server with a company token. Every operation is documented with request, variables and response in the [API Reference](https://docs.tnid.com/api-reference/002_company/002_ten_dlc).
{% endhint %}

## 1. Register a 10DLC API key

Brands and campaigns are submitted under a 10DLC (TCR) API key. Register the key with TNID once; it must be a key provisioned for this purpose only, not shared with another integration.

```graphql
mutation ($name: String!, $apiKey: String!, $apiKeySecret: String!) {
  addTenDlcApiKey(name: $name, apiKey: $apiKey, apiKeySecret: $apiKeySecret) {
    apiKeyId
    companyId
    name
  }
}
```

Related: `tenDlcApiKeys` lists your keys, `updateTenDlcApiKey` renames one, `deleteTenDlcApiKey` removes one.

**Sharing a key with a partner.** A company that owns a key can let another company register brands and campaigns under it with `authorizeTenDlcApiKey(apiKeyId, authorizedCompanyId)`, see the authorizations with `tenDlcApiKeyAuthorizations`, and withdraw with `revokeTenDlcApiKeyAuthorization`. The authorized company then sees those brands and campaigns with `ownershipType` other than `DIRECT`.

## 2. Brand lifecycle

| Status           | Meaning                                                                              |
| ---------------- | ------------------------------------------------------------------------------------ |
| `DRAFT`          | Saved in TNID, not sent to TCR. Edit freely.                                         |
| `PENDING_REVIEW` | Submitted to TCR, awaiting verification.                                             |
| `ACTIVE`         | Verified. Campaigns can be submitted against it.                                     |
| `REJECTED`       | TCR rejected the brand. Fix the data and resubmit with `updateAndSubmitTenDlcBrand`. |
| `EXPIRED`        | Registration lapsed.                                                                 |

Two ways in:

* **Draft first** (recommended when data arrives in pieces): `registerTenDlcBrandAsDraft(input)` creates a `DRAFT`; `updateTenDlcBrandAsDraft(id, input)` edits it; `updateAndSubmitTenDlcBrand(id, input)` submits it.
* **One shot:** `registerAndSubmitTenDlcBrand(input)` creates and submits in a single call.

```graphql
mutation ($input: TenDlcRegisterBrandInput!) {
  registerAndSubmitTenDlcBrand(input: $input) {
    id
    brandId
    displayName
    entityType
    status
  }
}
```

```json
{
  "input": {
    "companyId": "<your company id>",
    "tenDlcApiKeyId": "<id returned by addTenDlcApiKey>",
    "displayName": "Acme Metals",
    "companyName": "Acme Metals LLC",
    "entityType": "PRIVATE_PROFIT",
    "brandRelationship": "BASIC_ACCOUNT",
    "ein": "12-3456789",
    "einIssuingCountry": "US",
    "vertical": "PROFESSIONAL",
    "website": "https://acme-metals.example",
    "email": "compliance@acme-metals.example",
    "phone": "+14155551234",
    "street": "123 Main St",
    "city": "San Francisco",
    "state": "CA",
    "postalCode": "94105",
    "country": "US"
  }
}
```

The response `status` is `PENDING_REVIEW`; `brandId` is the TCR identifier (for example `BAXKPHB`). Poll `tenDlcBrand(id)` or list with `tenDlcBrands` until `status` is `ACTIVE`, then submit campaigns. `workflowStatus` (for example `VERIFIED`) reflects TCR's own verification workflow.

Allowed values for `entityType`, `brandRelationship`, `vertical` and the status enums are listed under [Enums](https://docs.tnid.com/api-reference/003_definitions/enums) in the API Reference.

## 3. Find and inspect brands

```graphql
query ($status: TenDlcBrandStatus, $ownershipType: TenDlcOwnershipType, $page: Int, $pageSize: Int) {
  tenDlcBrands(status: $status, ownershipType: $ownershipType, page: $page, pageSize: $pageSize) {
    page
    totalRecords
    records {
      id
      brandId
      displayName
      status
      workflowStatus
      ownershipType
    }
  }
}
```

10DLC lists use page-number pagination (`page`, `pageSize`, `totalRecords`) rather than the cursor style used elsewhere; see [Pagination](broken://pages/pagination). `tenDlcBrand(id)` returns one brand with its full address and contact fields. `deleteTenDlcBrand(id)` removes a brand from TNID.

## What to read next

* [10DLC Campaigns](/10dlc/campaigns): describe the messaging use case and attach numbers.
* Webhooks: get notified instead of polling.
* API Reference: [all 23 10DLC operations](https://docs.tnid.com/api-reference/002_company/002_ten_dlc).


# 10DLC Campaigns

Create, submit, update and monitor 10DLC campaigns through TNID, including submitting a campaign automatically once its brand is verified.

A campaign registers a messaging use case (marketing, customer care, 2FA and so on) under an `ACTIVE` [brand](/10dlc/brands). Numbers are attached to campaigns; carriers use the campaign to decide throughput and filtering.

{% hint style="info" %}
All 10DLC operations are on the `/company` GraphQL server with a company token. Every operation is documented with request, variables and response in the [API Reference](https://docs.tnid.com/api-reference/002_company/002_ten_dlc).
{% endhint %}

## Campaign lifecycle

| Status                 | Meaning                                                                   |
| ---------------------- | ------------------------------------------------------------------------- |
| `DRAFT`                | Saved in TNID, not sent to TCR.                                           |
| `PENDING`              | Submitted to TCR (or waiting for the brand to become `ACTIVE`).           |
| `ACTIVE`               | Approved. Numbers can be assigned.                                        |
| `ERROR`                | Submission failed. Fix and resubmit with `updateAndSubmitTenDlcCampaign`. |
| `EXPIRED`, `SUSPENDED` | No longer usable for traffic.                                             |

These are campaign statuses. Brand statuses are a different set (`PENDING_REVIEW` belongs to brands).

## Three ways to submit

| Mutation                                                                         | When to use it                                                                                                                       |
| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `registerAndSubmitTenDlcCampaign(input)`                                         | The brand is already `ACTIVE`. Creates the campaign and submits it to TCR immediately.                                               |
| `registerTenDlcCampaignWhenReady(input)`                                         | The brand is still under review. TNID stores the campaign as `PENDING` and submits it automatically when the brand becomes `ACTIVE`. |
| `saveTenDlcCampaignAsDraft(input)` then `updateTenDlcCampaignAsDraft(id, input)` | You want to build the campaign up over time before submitting.                                                                       |

```graphql
mutation ($input: TenDlcRegisterCampaignInput!) {
  registerAndSubmitTenDlcCampaign(input: $input) {
    campaignId
    brandId
    usecase
    status
    description
    messageFlow
    autoRenewal
  }
}
```

```json
{
  "input": {
    "tenDlcApiKeyId": "<id returned by addTenDlcApiKey>",
    "internalBrandId": "<TNID id of the brand, from tenDlcBrands.records.id>",
    "usecase": "MARKETING",
    "description": "Promotions and product launches for opted-in customers",
    "messageFlow": "Customers opt in by texting JOIN to our number or ticking the box at checkout. They receive up to 4 messages a month.",
    "sample1": "Acme: Summer sale starts today. Show this text in store for 10% off. Reply STOP to opt out, HELP for help.",
    "subscriberOptin": true,
    "subscriberOptout": true,
    "subscriberHelp": true,
    "termsAndConditions": true,
    "nnid": "<your NNID, if you have one>",
    "resellerId": "<CSP reseller id, if applicable>"
  }
}
```

`campaignId` (for example `CZDFL35`) is the TCR identifier. `updateAndSubmitTenDlcCampaign(id, input)` changes a submitted campaign and re-syncs it with TCR, which is also the path after an `ERROR`. `deleteTenDlcCampaign(id)` removes it.

## Monitor campaigns and their numbers

```graphql
query ($brandId: String, $status: TenDlcCampaignStatus, $usecase: String, $page: Int, $pageSize: Int) {
  tenDlcCampaigns(brandId: $brandId, status: $status, usecase: $usecase, page: $page, pageSize: $pageSize) {
    page
    totalRecords
    records {
      id
      campaignId
      brandId
      usecase
      status
      workflowStatus
      autoRenewal
      ownershipType
    }
  }
}
```

`tenDlcCampaign(id)` returns the full record (message flow, opt-in/opt-out/help settings, expiration and billing dates). `tenDlcCampaignWithTelephoneNumbers(id)` adds the numbers attached to the campaign with their carrier workflow statuses (`osrWorkflowStatus`, `tmobileWorkflowStatus`), which is the quickest way to see whether a number is cleared to send.

## Sending traffic

TNID registers the campaign. Sending messages on the registered numbers happens through your messaging provider; on TSG Global, assign the campaign to a number with the `campaign_id` field of the [SMS enablement endpoint](https://docs.tsgglobal.com/api-reference/phone-numbers/manage-number-for-sms-and-10dlc/enable-sms-10dlc-on-a-number).

## What to read next

* [10DLC Brands](/10dlc/brands): the brand must be `ACTIVE` first.
* Webhooks: get notified instead of polling.
* API Reference: [all 23 10DLC operations](https://docs.tnid.com/api-reference/002_company/002_ten_dlc).


# Changelog

Dated log of changes to the TNID API and to this documentation.

Newest first. API entries reflect the schema published in the [API Reference](https://docs.tnid.com/api-reference/), which is regenerated from the API source on every sync.

## 2026-09-11: documentation

* The generated schema reference now lives on this site under **API Reference**, with the same URL prefix for every operation. The old `tsg-global-inc.gitbook.io/tnid-docs-autogenerated` address still works.
* New guide pages: [API Quick Reference](/api-quick-reference), [AI Assistants & MCP](/ai-assistants-and-mcp), [Pagination](/pagination), Webhooks, [10DLC Brands](/10dlc/brands) and [10DLC Campaigns](/10dlc/campaigns) (replacing the "Coming soon" placeholders).
* Environments corrected: the retired `api.demo.v2.tnid.com` host removed, the zero environment added. Getting Started rewritten. Refresh Token request body fixed. Wrong query on List Pending Subscription Requests fixed. `Json` scalar corrected to `JSON`. Empty code tabs removed. Descriptions added to every page.

## 2026-09-09: API schema sync

* API Reference regenerated from the `tnid-v2-api` repository. Compared with the guides on this site it adds cursor-paginated twins for every list query (`paginated…`), email-based OTP login, 10DLC API keys, brands and campaigns, company webhooks, documents and AI tasks, forms, notifications, CSV user imports, message sentiment analysis, RCS information and AI suggestions, invitation links, client-secret management, subscription topics, settings and dashboard queries, `globalSearch`, and the `askDocsQuestion` assistant mutation.

## 2025-11-05

* Last edit to the hand-written guides before the 2026-09-11 review.


# 001\_user


# Get authentication OTP via email

HTTP API `POST` `/auth/create-user-otp`

To start using the API as a user you need to have a registered user account. The first thing you need to do to authenticate is to request an OTP code, in this case we'll demonstrate it using an email.

## Request

```json
{
  "email": "test1@email.com"
}
```

## Response

**HTTP Response Code:** 200

```json
{
  "expires_at": "2026-01-15T09:00:00.000000Z",
  "next_otp_at": "2026-01-15T09:00:01.000000",
  "otp_status": "OTP sent",
  "remaining_otp_attempts": 5
}
```


# Authenticate As User Via Email

HTTP API `POST` `/auth/token`

To start using the API as a user you need to have a registered user account. And you need to get an OTP code sent to your email address.

## Request

```json
{
  "email": "test1@email.com",
  "otp_code": "2"
}
```

## Response

**HTTP Response Code:** 200

```json
{
  "access_token": "eyJERAMPLE.PAYLOAD1.SIGNATURE",
  "refresh_token": "eyJERAMPLE.PAYLOAD2.SIGNATURE"
}
```


# Get authentication OTP via telephone number

HTTP API `POST` `/auth/create-user-otp`

To start using the API as a user you need to have a registered user account. The first thing you need to do to authenticate is to request an OTP code, in this case we'll demonstrate it using a telephone number.

## Request

```json
{
  "telephone_number": "1"
}
```

## Response

**HTTP Response Code:** 200

```json
{
  "expires_at": "2026-01-15T09:00:00.000000Z",
  "next_otp_at": "2026-01-15T09:00:01.000000",
  "otp_status": "OTP sent",
  "remaining_otp_attempts": 5
}
```


# Authenticate As User Via Telephone Number

HTTP API `POST` `/auth/token`

To start using the API as a user you need to have a registered user account. And you need to get an OTP code sent to your phone number.

## Request

```json
{
  "otp_code": "1",
  "telephone_number": "2"
}
```

## Response

**HTTP Response Code:** 200

```json
{
  "access_token": "eyJERAMPLE.PAYLOAD1.SIGNATURE",
  "refresh_token": "eyJERAMPLE.PAYLOAD2.SIGNATURE"
}
```


# Refresh Authentication (User)

HTTP API `POST` `/auth/refresh-token`

Access tokens have a limited lifetime, after which they expire and can no longer be used to access protected resources. To continue accessing these resources without requiring the user to re-authenticate, refresh tokens are used. A refresh token is a long-lived token that can be exchanged for a new access token.

## Request

```json
{
  "refresh_token": "eyJERAMPLE.PAYLOAD3.SIGNATURE"
}
```

## Response

**HTTP Response Code:** 200

```json
{
  "access_token": "eyJERAMPLE.PAYLOAD1.SIGNATURE",
  "refresh_token": "eyJERAMPLE.PAYLOAD2.SIGNATURE"
}
```


# 001\_profile


# Get User Profile

GraphQL endpoint: `/user`

Retrieves the authenticated user's own profile information.

## Query

```graphql
query {
  currentUser {
    id
    username
    documentRepositoryEmailAddress
    firstName
    middleName
    lastName
    birthdate
    aboutMe
    timezone
    metadata
    secretStorage
    addresses {
      city
      country
      state
      street
      types
      zipCode
      visibility
    }
    emails {
      email
      types
      visibility
    }
    telephoneNumbers {
      number
      types
      visibility
    }
    socialNetworks {
      type
      url
      visibility
    }
    webpages {
      type
      url
      visibility
    }
    interests {
      description
      title
    }
    nameVisibility
    aboutMeVisibility
    birthdateVisibility
    interestsVisibility
    timezoneVisibility
    metadataVisibility
  }
}

```

## Variables

```json
{}
```

## Response

```json
{
  "data": {
    "currentUser": {
      "aboutMe": "this is my generic intro",
      "aboutMeVisibility": [
        "PUBLIC"
      ],
      "addresses": [
        {
          "city": "test city",
          "country": "United States",
          "state": "Alabama",
          "street": "test street 1",
          "types": [
            "HOME"
          ],
          "visibility": [
            "PUBLIC"
          ],
          "zipCode": "12345"
        }
      ],
      "birthdate": "1990-01-15",
      "birthdateVisibility": [
        "PUBLIC"
      ],
      "documentRepositoryEmailAddress": "username2@parse.tnid.test",
      "emails": [
        {
          "email": "test3@email.com",
          "types": [
            "OTHER"
          ],
          "visibility": [
            "PUBLIC"
          ]
        }
      ],
      "firstName": "1john",
      "id": "00000000-0000-4000-8000-000000000001",
      "interests": [
        {
          "description": null,
          "title": "Interest 4"
        }
      ],
      "interestsVisibility": [
        "PUBLIC"
      ],
      "lastName": "2doe",
      "metadata": {
        "test": "value"
      },
      "metadataVisibility": [
        "PUBLIC"
      ],
      "middleName": "ray",
      "nameVisibility": [
        "PUBLIC"
      ],
      "secretStorage": {},
      "socialNetworks": [
        {
          "type": "FACEBOOK",
          "url": "https://test-sm5.not",
          "visibility": [
            "PUBLIC"
          ]
        }
      ],
      "telephoneNumbers": [
        {
          "number": "6",
          "types": [
            "OTHER"
          ],
          "visibility": [
            "PUBLIC"
          ]
        }
      ],
      "timezone": "UTC",
      "timezoneVisibility": [
        "PUBLIC"
      ],
      "username": "username2",
      "webpages": [
        {
          "type": "WEBPAGE",
          "url": "https://test-webpage7.not",
          "visibility": [
            "PUBLIC"
          ]
        }
      ]
    }
  }
}
```


# Update user profile

GraphQL endpoint: `/user`

Updates the user's profile information including basic details, contact information, and visibility settings. This mutation allows comprehensive updates to user profile data.

## Query

```graphql
mutation (
  $username: String
  $firstName: String
  $lastName: String
  $middleName: String
  $birthdate: Date
  $aboutMe: String
  $timezone: String
  $metadata: JSON
  $secretStorage: JSON
  $emails: [UserEmailInput]
  $telephoneNumbers: [UserTelephoneNumberInput!]
  $addresses: [UserAddressInput]
  $webpages: [UserWebpageInput]
  $socialNetworks: [UserSocialMediaInput]
  $userInterests: [ID]
  $nameVisibility: [UserVisibilityType!]
  $profileImageUrlVisibility: [UserVisibilityType!]
  $aboutMeVisibility: [UserVisibilityType!]
  $birthdateVisibility: [UserVisibilityType!]
  $interestsVisibility: [UserVisibilityType!]
  $timezoneVisibility: [UserVisibilityType!]
  $metadataVisibility: [UserVisibilityType!]
) {
  updateUser (
    username: $username
    firstName: $firstName
    lastName: $lastName
    middleName: $middleName
    birthdate: $birthdate
    aboutMe: $aboutMe
    timezone: $timezone
    metadata: $metadata
    secretStorage: $secretStorage
    emails: $emails
    telephoneNumbers: $telephoneNumbers
    addresses: $addresses
    webpages: $webpages
    socialNetworks: $socialNetworks
    userInterests: $userInterests
    nameVisibility: $nameVisibility
    profileImageUrlVisibility: $profileImageUrlVisibility
    aboutMeVisibility: $aboutMeVisibility
    birthdateVisibility: $birthdateVisibility
    interestsVisibility: $interestsVisibility
    timezoneVisibility: $timezoneVisibility
    metadataVisibility: $metadataVisibility
  ) {
    id
    metadata
    secretStorage
  }
}

```

## Variables

```json
{
  "aboutMe": "updated about me",
  "aboutMeVisibility": [
    "FAMILY"
  ],
  "addresses": [
    {
      "city": "test city",
      "country": "United States",
      "state": "Alabama",
      "street": "test street 3",
      "types": [
        "WORK"
      ],
      "visibility": [
        "FAMILY"
      ],
      "zipCode": "12345"
    },
    {
      "city": "test city",
      "country": "United States",
      "state": "Alabama",
      "street": "test street 4",
      "types": [
        "WORK"
      ],
      "visibility": [
        "FAMILY"
      ],
      "zipCode": "12345"
    }
  ],
  "birthdate": "2000-01-01",
  "birthdateVisibility": [
    "FAMILY"
  ],
  "emails": [
    {
      "email": "test1@email.com",
      "id": "00000000-0000-4000-8000-000000000002",
      "types": [
        "WORK"
      ],
      "visibility": [
        "FAMILY"
      ]
    },
    {
      "email": "test2@email.com",
      "types": [
        "WORK"
      ],
      "visibility": [
        "FAMILY"
      ]
    }
  ],
  "firstName": "updated-first_name",
  "interestsVisibility": [
    "FAMILY"
  ],
  "lastName": "updated-last_name",
  "metadata": {
    "test": "updated-value"
  },
  "metadataVisibility": [
    "FAMILY"
  ],
  "middleName": "updated-middle_name",
  "nameVisibility": [
    "FAMILY"
  ],
  "profileImageUrlVisibility": [
    "FAMILY"
  ],
  "socialNetworks": [
    {
      "type": "FACEBOOK",
      "url": "https://test-sm9.not",
      "visibility": [
        "FAMILY"
      ]
    },
    {
      "type": "FACEBOOK",
      "url": "https://test-sm10.not",
      "visibility": [
        "FAMILY"
      ]
    }
  ],
  "telephoneNumbers": [
    {
      "country_code": "1",
      "id": "00000000-0000-4000-8000-000000000003",
      "number": "7",
      "types": [
        "WORK"
      ],
      "visibility": [
        "FAMILY"
      ]
    },
    {
      "country_code": "1",
      "number": "8",
      "types": [
        "WORK"
      ],
      "visibility": [
        "FAMILY"
      ]
    }
  ],
  "timezone": "Asia/Phnom_Penh",
  "timezoneVisibility": [
    "FAMILY"
  ],
  "userInterests": [
    "00000000-0000-4000-8000-000000000004"
  ],
  "username": "updated-username",
  "webpages": [
    {
      "type": "WEBPAGE",
      "url": "https://test-webpage5.not",
      "visibility": [
        "FAMILY"
      ]
    },
    {
      "type": "WEBPAGE",
      "url": "https://test-webpage6.not",
      "visibility": [
        "FAMILY"
      ]
    }
  ]
}
```

## Response

```json
{
  "data": {
    "updateUser": {
      "id": "00000000-0000-4000-8000-000000000001",
      "metadata": {
        "test": "updated-value"
      },
      "secretStorage": {}
    }
  }
}
```


# 002\_email


# Create User Email

GraphQL endpoint: `/user`

Creates a new email address for the user. The email is added to the user's profile with the specified types and visibility settings. The email starts as unverified.

## Query

```graphql
mutation (
  $email: String!
  $types: [UserEmailType!]
  $visibility: [UserVisibilityType!]
) {
  createUserEmail (
    email: $email
    types: $types
    visibility: $visibility
  ) {
    id
    email
    types
    visibility
  }
}

```

## Variables

```json
{
  "email": "test1@email.com",
  "types": [
    "HOME"
  ],
  "visibility": [
    "FAMILY"
  ]
}
```

## Response

```json
{
  "data": {
    "createUserEmail": {
      "email": "test1@email.com",
      "id": "00000000-0000-4000-8000-000000000001",
      "types": [
        "HOME"
      ],
      "visibility": [
        "FAMILY"
      ]
    }
  }
}
```


# Create user email verification code

GraphQL endpoint: `/user`

Generates a verification code (OTP) for an unverified email address. The verification code will be sent to the email address and can be used to verify ownership.

## Query

```graphql
mutation (
  $emailId: ID!
) {
  createUserEmailVerificationCode (
    emailId: $emailId
  ) {
    expiresAt
    remainingOtpAttempts
    nextOtpAt
    otpStatus
  }
}

```

## Variables

```json
{
  "emailId": "00000000-0000-4000-8000-000000000001"
}
```

## Response

```json
{
  "data": {
    "createUserEmailVerificationCode": {
      "expiresAt": "2026-01-15T09:00:00.000000",
      "nextOtpAt": "2026-01-15T09:00:01.000000",
      "otpStatus": "OTP sent",
      "remainingOtpAttempts": 5
    }
  }
}
```


# Verify User Email

GraphQL endpoint: `/user`

Verifies a user's email address using the verification code (OTP) that was sent to the email. Once verified, the email address status changes to verified and can be set as primary.

## Query

```graphql
mutation (
  $emailId: ID!
  $code: String!
) {
  verifyUserEmail (
    emailId: $emailId
    code: $code
  ) {
    status
  }
}

```

## Variables

```json
{
  "code": "1",
  "emailId": "00000000-0000-4000-8000-000000000001"
}
```

## Response

```json
{
  "data": {
    "verifyUserEmail": {
      "status": "OK"
    }
  }
}
```




---

[Next Page](/llms-full.txt/1)

