> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nekt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Visora as a data source

> Bring your Visora WhatsApp tickets, messages, connections, queues and agents to Nekt.

Visora, by BP Tecnologia, is a WhatsApp customer service platform: conversations are organized in tickets (atendimentos), routed to queues (filas) and handled by agents (atendentes) on one or more WhatsApp connections. This connector reads your company's data through the official Visora Integration API and brings your tickets, with their status, queue, agent, contact, tags and deals, the full history of their messages, and your WhatsApp connections, queues and agents.

> [!NOTE]
>
> The connector only reads data. It never sends messages or creates, changes or deletes anything in Visora.

## Configuring Visora as a Source

In the [Sources](https://app.nekt.ai/sources) tab, click on the "Add source" button located on the top right of your screen. Then, select the Visora option from the list of connectors.

Click **Next** and you'll be prompted to add your access.

### 1. Add account access

The Visora Integration API uses an **API key**. To create one, open Visora and go to **Administração > Chaves de API**, click **Nova chave**, select **every WhatsApp connection** you want to sync, choose how long the key is valid and copy the key. It starts with `vsk_live_` and is shown only once.

* **API Key**: the key you created.
* **Start Date**: the earliest date synced. Tickets created or with a message since this date are loaded, and so are the messages sent since this date.
* **Message Lookback (days)** (advanced, default 2): how many days before the newest message of the previous sync each sync reads again. It picks up messages deleted after they were loaded, internal notes, and conversations that were very busy while a sync ran. From 1 to 30 (it cannot be turned off); higher values make each sync longer.

> [!WARNING]
>
> The key only reads the WhatsApp connections selected in it. Tickets and messages of the other connections are not synced. Each sync log lists the connections the key reads. If you add a connection to the key later, its messages since the Start Date are loaded in the next sync.

> [!TIP]
>
> Create a key only for Nekt. Visora allows 300 requests per minute per key, and a key shared with other integrations makes the syncs slower. The connection token found under Conexões does not work with this connector.

Once you're done, click **Next**.

### 2. Select streams

Choose which data streams you want to sync. For faster extractions, select only the streams that are relevant to your analysis. You can select entire groups of streams or pick specific ones.

> Tip: The stream can be found more easily by typing its name.

Select the streams and click **Next**.

### 3. Configure data streams

Customize how you want your data to appear in your catalog. Select the desired layer where the data will be placed, a folder to organize it inside the layer, a name for each table (which will effectively contain the fetched data) and the type of sync.

* **Layer**: choose between the existing layers on your catalog. This is where you will find your new extracted tables as the extraction runs successfully.
* **Folder**: a folder can be created inside the selected layer to group all tables being created from this new data source.
* **Table name**: we suggest a name, but feel free to customize it. You have the option to add a **prefix** to all tables at once and make this process faster!
* **Sync Type**: keep the suggested type. Messages is **Incremental**: each sync reads only the messages since the previous one and merges them into the table. The other tables are **Full table**: the Visora API cannot list tickets changed since a date (a ticket can be closed, transferred or tagged without a new message), so each sync reads them whole and replaces the table.

Once you are done configuring, click **Next**.

### 4. Configure data source

Describe your data source for easy identification within your organization, not exceeding 140 characters.

To define your [Trigger](https://docs.nekt.com/runs/scheduling-and-triggers.md), consider how often you want data to be extracted from this source. This decision usually depends on how frequently you need the new table data updated (every day, once a week, or only at specific times). Messages are read incrementally, so an hourly or daily sync works well. Tickets are read whole on every sync, so syncs take longer when the Start Date is far back.

Optionally, you can configure Delta Log Retention and determine for how long we should store old states of this table as it gets updated. Read more about this resource [here](https://docs.nekt.com/get-started/core-concepts/resource-control).

Once you are ready, click **Next** to finalize the setup.

### 5. Check your new source

You can view your new source on the [Sources](https://app.nekt.ai/sources) page. If needed, manually trigger the source extraction by clicking on the arrow button. Once executed, your data will appear in your Catalog.

> [!WARNING]
>
> For you to be able to see it on your [Catalog](https://app.nekt.ai/catalog), you need at least one successful source run.

# Streams and Fields

## Available streams

The table below lists every stream, its **slug** (the exact identifier to pass when creating the source via API) and a short description.

| Stream | Slug | Sync | Description |
| - | - | - | - |
| Connections | `connections` | Full table | WhatsApp connections the API key reads. |
| Queues | `queues` | Full table | Queues (filas) of the company. |
| Agents | `users` | Full table | Agents (atendentes) of the company. |
| Tickets | `tickets` | Full table | Tickets (conversations) created or with a message since the Start Date, with status, connection, queue, agent, contact, tags, group flag and deals. |
| Messages | `messages` | Incremental | Every message of the tickets, sent and received, internal notes included, with the contact's name and number. |

To build a message history with the ticket status and tags, join `messages.ticket_id` with `tickets.id`.

## Fields by stream

Fields marked String (JSON) hold a nested list as JSON text; use your warehouse's JSON functions to read them.

> [accordion] Connections
>
> The WhatsApp connections the API key can read.
>
> Table: `connections` · Primary key: `id` · Sync: Full table
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Unique identifier of the WhatsApp connection in Visora (the `connection_id` of tickets and messages). |
> | `name` | String | Name of the WhatsApp connection, as shown in Visora. |

> [accordion] Queues
>
> The queues (filas) of the company.
>
> Table: `queues` · Primary key: `id` · Sync: Full table
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Unique identifier of the queue in Visora (the `queue_id` of tickets and messages). |
> | `name` | String | Name of the queue, as shown in Visora. |

> [accordion] Agents
>
> The agents (atendentes) of the company. The Visora API provides only their id and name.
>
> Table: `users` · Primary key: `id` · Sync: Full table
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Unique identifier of the agent in Visora (the `user_id` of tickets and messages). |
> | `name` | String | Name of the agent, as shown in Visora. |

> [accordion] Tickets
>
> Tickets (atendimentos, conversations) created since the Start Date, plus older tickets that got a message since then.
>
> Table: `tickets` · Primary key: `id` · Sync: Full table
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Unique identifier of the ticket in Visora. |
> | `uuid` | String | Public UUID of the ticket. |
> | `status` | String | Current status of the ticket (conversation status): `open`, `pending`, `closed`, `group`, `ai`, `api`, `nps` or `lgpd`. |
> | `connection_id` | Integer | Identifier of the WhatsApp connection of the ticket. |
> | `connection_name` | String | Name of the WhatsApp connection of the ticket. |
> | `queue_id` | Integer | Identifier of the queue the ticket is currently in; empty when it has none. |
> | `queue_name` | String | Name of the queue the ticket is currently in. |
> | `user_id` | Integer | Identifier of the agent currently assigned to the ticket; empty when unassigned. |
> | `user_name` | String | Name of the agent currently assigned to the ticket. |
> | `contact_id` | Integer | Identifier of the contact (or WhatsApp group) of the ticket. |
> | `contact_name` | String | Name of the contact (or WhatsApp group). |
> | `contact_number` | String | Phone number of the contact, digits only with country and area code (or the group identifier). |
> | `is_group` | Boolean | Whether the ticket is a WhatsApp group conversation. |
> | `tags` | String (JSON) | Tags of the ticket, as a list of objects with `id` and `name`. |
> | `tag_names` | Array of strings | Names of the tags of the ticket. |
> | `created_at` | Datetime | Date and time the ticket was created. |
> | `closed_at` | Datetime | Date and time the ticket was closed; empty while it is open, and sometimes empty on closed tickets when Visora did not record it. |
> | `last_message` | String | Text of the last message of the ticket. |
> | `last_message_created_at` | Datetime | Date and time of the last message of the ticket. |
> | `unread_messages` | Integer | Number of messages of the ticket not read yet by the agents. |
> | `deals` | String (JSON) | Deals (negócios) linked to the ticket, as a list of objects with `id`, `title`, `pipeline`, `stage`, `status` and `value`. |

> [accordion] Messages
>
> Every message of the tickets, sent by the company or by the contact, including internal notes.
>
> Table: `messages` · Primary key: `id` · Sync: Incremental (`created_at`)
>
> | Field | Type | Description |
> | :- | :- | :- |
> | `id` | Integer | Unique identifier of the message in Visora. |
> | `ticket_id` | Integer | Identifier of the ticket of the message; join with `tickets.id` for its status, tags and group flag. |
> | `created_at` | Datetime | Date and time the message was created. Used as the incremental replication key. |
> | `from_me` | Boolean | True when the message was sent by the company (an agent, a bot or the API); false when it was sent by the contact. |
> | `body` | String | Text of the message; empty for a message erased with Visora's LGPD option. |
> | `media_type` | String | Type of the attached media (image, audio, video, document...); empty for text messages. |
> | `is_private` | Boolean | True for an internal note, visible only to the company's agents. |
> | `is_deleted` | Boolean | True when the message was deleted. |
> | `connection_id` | Integer | Identifier of the WhatsApp connection of the message. |
> | `connection_name` | String | Name of the WhatsApp connection of the message. |
> | `queue_id` | Integer | Identifier of the queue the message's ticket is currently in. |
> | `queue_name` | String | Name of the queue the message's ticket is currently in. |
> | `user_id` | Integer | Identifier of the agent of the message; empty for messages of the contact or of bots. |
> | `user_name` | String | Name of the agent of the message. |
> | `contact_id` | Integer | Identifier of the contact (or WhatsApp group) the message belongs to. |
> | `contact_name` | String | Name of the contact (or WhatsApp group), as listed on its tickets. |
> | `contact_number` | String | Phone number of the contact, digits only with country and area code (or the group identifier). |

## Implementation Notes

### Data Quality Considerations

* **How messages stay up to date.** Each sync finds the tickets that got a message since the previous sync (minus the Message Lookback), up to the moment the sync started, and reads the messages of their contacts in that period. A conversation that started months ago and gets a message today has its new messages added. A conversation that gets a new message while the sync is listing tickets is still read. A message that arrives while a sync is running is read by the next sync. If a sync fails, the next one reads the same period again. On a very busy account, if the conversations keep changing while they are listed, the sync log says so and the next sync reads the conversations that may have been missed, from the same start. If that happens in three incremental syncs in a row, the Messages table is shown as failed with a message saying so; scheduling the source at a quieter time of day solves it. A first sync or a full sync only warns. A message that only shows up in Visora after the Message Lookback has passed (for example, saved with an older date) is only loaded by a full sync.
* **Deleted messages.** A message deleted within the Message Lookback (2 days by default) after it was loaded is updated (`is_deleted`, and an empty `body` for LGPD erasure). The lookback counts from the newest message loaded by the previous sync. Later deletions are only picked up by a full sync.
* **Message delivery status is not available.** The Visora API does not provide whether a message was delivered or read.
* **Queue of a message.** Visora sends the current queue of the message's ticket, not the queue at the time the message was sent.
* **Media.** Only the media type is loaded. Visora's media links expire one hour after they are read, so they are not stored.
* **Connections outside the key.** Tickets and messages of WhatsApp connections not selected in the API key are not synced. When a connection is added to the key, the next sync loads its messages since the Start Date.
* **Full table tables are never half updated.** The connector reads all the data of a full table table before writing any of it. If Visora fails while it is read, nothing of that table is sent and the sync shows it as failed. On GCP (BigQuery) the table keeps its previous content; on AWS, a full table table whose stream failed may be emptied by the load step until the next successful sync. A full table table that suddenly comes back empty is not emptied either: the sync fails for that table. If the data really was removed in Visora, run a full sync of the source to accept it.
* **Internal notes.** Visora may not count an internal note as the ticket's last message. A note added to a ticket with no other activity is then loaded only if the ticket gets another message while the note is still within the Message Lookback, or in a full sync.
* **Deleted contacts.** If a contact is deleted in Visora while a sync runs, its messages are no longer available; the sync log says how many.

### API Limits & Performance

* Visora allows up to 300 requests per minute per API key. The connector makes at most 240 per minute and, when Visora asks it to slow down, waits and tells you in the sync log.
* Visora lists messages one contact at a time, so each sync makes at least one request per contact with recent activity, plus one per additional 200 messages.
* The first sync reads every contact active since the Start Date in a single run: about 45 minutes for 10,000 contacts, 3.5 hours for 50,000 and 7 hours for 100,000. The sync log says how long it will take at least when there are 2,000 contacts or more. If the first sync fails, the next one starts it again, so choose a Start Date that keeps it manageable.
* Tickets are read 100 per request, twice (by creation date and by last message date), on every sync. An older Start Date makes every sync longer.
* **Recommended schedule: daily.** Each sync reads again every contact active within the Message Lookback (2 days by default): with hourly syncs, an active contact is read up to 48 times in two days; with daily syncs, two or three times. Small accounts can sync every few hours.

## Troubleshooting

| Error or symptom | What it means | What to do |
| :- | :- | :- |
| "Visora rejected the API key of this source" | The key is wrong, revoked or expired, or a connection token was used instead of an API key. | Create an API key under Administração > Chaves de API, selecting every WhatsApp connection, and update the source. |
| "Visora refused access because the company's Visora subscription is expired" | The company's Visora subscription is not active. | Renew the subscription with Visora; the next sync reads normally. |
| "The Visora API key of this source has no WhatsApp connection" | No connection is selected in the key. | Edit the key in Visora and select every WhatsApp connection. |
| A WhatsApp connection is missing from the tables | The key does not include that connection. The sync log lists the connections the key reads. | Add the connection to the key; the next sync loads its messages since the Start Date. |
| "Visora is limiting how fast this sync may read" | The request limit of the key was reached, possibly together with other integrations using it. | No action needed; the sync waits and continues. Use a key only for Nekt. |
| "Visora returned no ... records at all, although the previous sync read N" | Visora returned an empty list for a table that had rows. The table was kept. | If the data really was removed, run a full sync of the source. Otherwise run the sync again later. |
| "Visora was very busy while conversations were listed" | Conversations kept changing while the sync listed them; a few may not have been read yet. | No action needed; the next sync reads them. |
| "Visora's list of conversations kept changing while it was read, in 3 syncs in a row" | The Messages table was not updated in this sync, so no conversation is missed. | Schedule the source at a quieter time of day (for example early morning or late evening); the next sync that lists the conversations completely loads everything missed. If it keeps happening, contact Nekt support. |
| "Could not reach Visora" or "Visora kept failing" | Visora did not answer, or answered with errors, after several attempts. | Usually temporary; the next sync tries again. |
| "Visora answered ... in a format this connector does not recognize" or "Visora sent the same ... page twice" | The Visora API changed or answered in an unexpected way. | Contact Nekt support. |

## Skills for agents

<Snippet file="agent-skills-intro.mdx" />

> [card] [Download Visora skills file](https://docs.nekt.com/sources/visora.md)
>
> Visora connector documentation as plain markdown, for use in AI agent contexts.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.