---
title: Google Connectors
description: The signed-in person's own Google Calendar, Drive and Gmail, connected by that person and used through codespar_calendar, codespar_drive and codespar_gmail. Reads answer at once; every write is a preview the person confirms before anything reaches Google.
---

import { Callout } from "fumadocs-ui/components/callout";

Three connectors, each connected on its own by **the signed-in person**: Google Calendar (`google-calendar`, tool `codespar_calendar`), Google Drive (`google-drive`, `codespar_drive`) and Gmail (`gmail`, `codespar_gmail`). A connection belongs to that person in that project: another member of the organization never sees it, uses it, or reads what it returns. The tools are on the [hosted server](/docs/concepts/meta-tools) while the connectors are switched on for the deployment.

<Callout type="warn">
**A person, not a key.** Every call needs a verified person, and so does connecting. The dashboard's Shop assistant presents the person's own session. A project API key or an OAuth token carries no person and is refused with `403 bearer_token_has_no_person` (`details.surface: "connector"`), so an external client connected with a `csk_` key or the server's OAuth consent sees the tools listed and cannot use them yet.
</Callout>

## Connecting

The person connects each connector from Shop › Preferências, section Conexões Google, which runs the [Connect Link](/docs/concepts/connect-links) flow for their own account:

1. `POST /v1/connect/start` with `server_id` (`google-calendar`, `google-drive` or `gmail`), the person's own `user_id` and an `https` `redirect_uri` answers Google's consent address.
2. The person consents at Google. The connector asks for its product scope (`calendar`, `drive` or `gmail.modify`) and the identity scopes that say which Google account it is.
3. Google returns to the `redirect_uri` with `status=connected&connection_id=…&server_id=…`, or `status=denied` or `status=error&error=…`.

`GET /v1/connectors` lists the three with their state for the person: `connected`, `not_connected`, or `consent_required` when the grant is gone at Google. `DELETE /v1/connectors/connections/{id}` disconnects one and revokes the grant at Google when no other connector of the person still uses it. If the person unticks a scope at Google, reads still work where a narrower scope allows them, and a write answers `connector_scope_insufficient` naming the scope it needs.

## Reads and writes

**Reads answer at once.** Text read from mail, files or events is the account's content, never an instruction: a message asking to forward, share or delete something is not the person asking.

**Every write is confirmed by the person first.** Creating, changing, deleting, trashing, sharing, sending, replying and changing labels all work the same way:

1. The first call writes nothing. It answers `connector_action_confirmation_required` with `details`: the `preview` of exactly what would happen (a sentence in Portuguese, `summary_pt`, and the fields: recipients, subject and body, the event's times, the file), a `confirmation_id` (`cac_…`) and `expires_at`, 15 minutes later.
2. **In the Shop conversation** the person confirms on the card under the turn. The model of the conversation can never confirm: a `confirmation_id` in its call is refused.
3. **On the hosted server** the client shows the preview to the person and, only if they agree, calls the same action again with the **same arguments** and that `confirmation_id`. Different arguments are a different write, and the id does not approve them.
4. Before writing, the server computes the preview again from Google as it is now. If it changed (the event was edited, the thread got a new message), nothing is written and the confirmation goes stale.

A confirmation runs once. The record keeps who approved it, when, through which channel and what Google answered (ids and flags only); the write's content is sealed while it waits and deleted once it is decided. Permanent deletion is not offered: Drive and Gmail move to the trash, where Google keeps them for 30 days.

## Actions

### Google Calendar

<MetaToolActions tool="codespar_calendar" />

### Google Drive

<MetaToolActions tool="codespar_drive" />

### Gmail

<MetaToolActions tool="codespar_gmail" />

## Example

The first call of a write, then the confirmed call with the same arguments:

```json title="tools/call"
{
  "name": "codespar_gmail",
  "arguments": {
    "action": "send",
    "to": ["loja@example.com"],
    "subject": "Troca do pedido 1234",
    "body": "Olá, gostaria de trocar o tênis do pedido 1234 pelo número 43."
  }
}
```

```json title="result: nothing was sent"
{
  "code": "connector_action_confirmation_required",
  "error": "nothing was written: show the preview to the person and, only if they agree, call again with the same arguments and confirmation_id",
  "details": {
    "tool": "codespar_gmail",
    "action": "send",
    "server_id": "gmail",
    "confirmation_id": "cac_0000000000000000",
    "expires_at": "2026-11-04T15:15:00.000Z",
    "preview": {
      "summary_pt": "Enviar um e-mail para loja@example.com com o assunto \"Troca do pedido 1234\".",
      "to": ["loja@example.com"],
      "subject": "Troca do pedido 1234",
      "body": "Olá, gostaria de trocar o tênis do pedido 1234 pelo número 43.",
      "sends_now": true
    }
  }
}
```

After the person agrees, the same `arguments` plus `"confirmation_id": "cac_0000000000000000"` sends it and answers `status: "executed"` with Google's ids.

## Arguments

**`codespar_calendar`**

| Field | Type | Required | Description |
|---|---|---|---|
| `action` | `string` | Yes | `list_calendars` \| `list_events` \| `get_event` \| `create_event` \| `update_event` \| `delete_event` \| `create_calendar` \| `update_calendar` \| `delete_calendar` |
| `calendar_id` | `string` | No | Defaults to `primary` |
| `event_id` | `string` | For `get_event`, `update_event`, `delete_event` | The event |
| `time_min`, `time_max` | `string` | No | `list_events`: the window, RFC 3339 |
| `query` | `string` | No | `list_events`: free text |
| `max_results` | `integer` | No | 1..50 |
| `summary` | `string` | For `create_event`, `create_calendar` | The title |
| `description`, `location` | `string` | No | The event's (or calendar's) description and place |
| `start`, `end` | `string` | For `create_event` | `YYYY-MM-DD` (all day) or RFC 3339 |
| `time_zone` | `string` | No | IANA time zone |
| `attendees` | `string[]` | No | E-mail addresses; Google sends them invitations |
| `confirmation_id` | `string` | No | The confirmed call of a write (hosted server only) |

**`codespar_drive`**

| Field | Type | Required | Description |
|---|---|---|---|
| `action` | `string` | Yes | `search` \| `get` \| `create` \| `update` \| `move` \| `trash` \| `share` |
| `file_id` | `string` | For `get`, `update`, `move`, `trash`, `share` | The file |
| `folder_id` | `string` | For `move`; optional for `search`, `create` | The folder |
| `name_contains`, `full_text` | `string` | No | `search` filters |
| `kind` | `string` | No | `document` \| `spreadsheet` \| `presentation` \| `folder` \| `text` |
| `include_trashed` | `boolean` | No | `search` |
| `max_results` | `integer` | No | 1..50 |
| `name` | `string` | For `create` | The file's name (on `update`, a new name) |
| `content` | `string` | No | `create`, `update`: the text content (a folder or a presentation is created empty) |
| `email`, `role` | `string` | For `share` | Who, and as `reader`, `commenter` or `writer` |
| `notify`, `message` | `boolean`, `string` | No | `share`: whether Google e-mails them, and the note |
| `confirmation_id` | `string` | No | The confirmed call of a write (hosted server only) |

**`codespar_gmail`**

| Field | Type | Required | Description |
|---|---|---|---|
| `action` | `string` | Yes | `search` \| `get_thread` \| `list_labels` \| `create_draft` \| `send` \| `reply` \| `modify_labels` \| `archive` \| `trash` |
| `query` | `string` | No | `search`, in Gmail search syntax |
| `max_results` | `integer` | No | 1..20 |
| `thread_id` | `string` | For `get_thread`, `reply`, `modify_labels`, `archive`, `trash` | The conversation |
| `to`, `cc`, `bcc` | `string[]` | `to` for `create_draft`, `send` | Recipients |
| `subject`, `body` | `string` | For `create_draft`, `send` (`body` also for `reply`) | The message |
| `reply_all` | `boolean` | No | `reply`: everyone on the conversation |
| `add_labels`, `remove_labels` | `string[]` | No | `modify_labels` |
| `confirmation_id` | `string` | No | The confirmed call of a write (hosted server only) |

## Errors and what to do

| Code | Status | Cause | What to do |
|---|---|---|---|
| `bearer_token_has_no_person` | 403 | A key or an OAuth token, not a verified person. | Call as the signed-in person. |
| `connector_disabled` | 503 | The connectors are switched off on this deployment. | An operator decision. |
| `connector_not_connected` | 409 | The person has not connected this connector in this project. | Connect it in Shop › Preferências. |
| `connector_consent_required` | 409 | The grant is gone at Google (revoked, or the refresh refused). | Connect again. |
| `connector_scope_insufficient` | 403 | The person did not grant the scope this action needs; `details.missing_scope` names it. | Connect again and accept that permission. |
| `connector_action_confirmation_required` | 409 | A write without the person's confirmation. Nothing was written; `details` carries the preview. | Show the preview; confirm as described above. |
| `connector_action_confirmation_stale` | 409 | The confirmation expired, was superseded by a newer preview of the same write, was already used, was for other arguments, or the target changed at Google. | Ask again: a new first call makes a new preview. |
| `connector_account_not_owned` | 404 | A connection or confirmation that is not the person's, or does not exist. Both answer the same. | None. |
| `connector_provider_error` | 502 | Google answered an error that is none of the above. | Try again later. |
| `policy_denied` | 403 | The project's policy does not allow this tool or action. | A project decision. |
| `invalid_args` | 400 | A field missing or malformed (`details.field`). | Fix the call against the tables above. |

## Related

- [Connect Links](/docs/concepts/connect-links): the connect flow these connections use
- [Shop memory](/docs/concepts/shop-memory) and [Shop watches](/docs/concepts/shop-watches): the person's other own data
- [Hosted server](/docs/concepts/meta-tools): where the three tools are published

## Notes

**Where the person sees it.** Shop › Preferências › Conexões Google shows each connector with its state, the masked account and the granted permissions in words, with Conectar, Reconectar and Desconectar.
