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

# Libraries and HTTP Clients

> Ready-to-use code snippets for integrating with the Vendaze API in Node.js, Python, and Go.

There are no official Vendaze SDKs. The API follows standard REST and HTTP conventions and works with any HTTP client. The snippets below cover the most common operations in three languages. Copy, adapt, and ship.

<Note>
  All examples assume you have already completed the OAuth flow and have a valid `access_token` and
  `refresh_token`. See [Authentication](/en/guides/authentication) if you have not done that yet.
</Note>

## Node.js

Uses the built-in `fetch` API (Node.js 18+). No dependencies required.

<Tabs>
  <Tab title="OAuth token exchange">
    ```javascript theme={null}
    // Exchange an authorization code for tokens
    async function exchangeCode({ code, clientId, clientSecret }) {
      const res = await fetch('https://api.vendaze.com/oauth/token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({
          grant_type: 'authorization_code',
          code,
          client_id: clientId,
          client_secret: clientSecret,
        }),
      });

      if (!res.ok) {
        const { error } = await res.json();
        throw new Error(`${error.code}: ${error.message}`);
      }

      return res.json();
      // { access_token, refresh_token, token_type, expires_in, workspace_slug }
    }
    ```
  </Tab>

  <Tab title="Refresh access token">
    ```javascript theme={null}
    // Refresh an expired access token
    async function refreshToken({ refreshToken, clientId, clientSecret }) {
      const res = await fetch('https://api.vendaze.com/oauth/token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({
          grant_type: 'refresh_token',
          refresh_token: refreshToken,
          client_id: clientId,
          client_secret: clientSecret,
        }),
      });

      if (!res.ok) {
        const { error } = await res.json();
        throw new Error(`${error.code}: ${error.message}`);
      }

      return res.json();
    }
    ```
  </Tab>

  <Tab title="Get a person by ID">
    ```javascript theme={null}
    // Fetch a person by ID
    async function getPerson(accessToken, personId) {
      const res = await fetch(
        `https://api.vendaze.com/v1/people/${personId}`,
        { headers: { Authorization: `Bearer ${accessToken}` } }
      );

      if (!res.ok) {
        const { error } = await res.json();
        throw new Error(`${error.code}: ${error.message}`);
      }

      const { data } = await res.json();
      return data;
    }
    ```
  </Tab>

  <Tab title="Create a person">
    ```javascript theme={null}
    // Create a new contact in the workspace
    async function createPerson(accessToken, payload) {
      const res = await fetch('https://api.vendaze.com/v1/people', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': crypto.randomUUID(),
        },
        body: JSON.stringify(payload),
      });

      if (!res.ok) {
        const { error } = await res.json();
        if (error.code === 'validation_error') {
          console.error('Validation failed:', error.fields);
        }
        throw new Error(`${error.code}: ${error.message}`);
      }

      const { data } = await res.json();
      return data;
    }

    // Usage
    const person = await createPerson(accessToken, {
      full_name: 'Ana Costa',
      emails: [{ email: 'ana@company.com', type: 'work' }],
      phones: [{ phone: '+5511999990001', type: 'mobile' }],
      position: 'Head of Sales',
    });
    ```
  </Tab>

  <Tab title="Error handling wrapper">
    ```javascript theme={null}
    // Reusable API client with automatic token refresh
    class VendazeClient {
      constructor({ accessToken, refreshToken, clientId, clientSecret, onTokenRefresh }) {
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.onTokenRefresh = onTokenRefresh; // callback to persist new tokens
      }

      async request(path, options = {}) {
        const res = await fetch(`https://api.vendaze.com${path}`, {
          ...options,
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
            'Content-Type': 'application/json',
            ...options.headers,
          },
        });

        if (res.status === 401) {
          const { error } = await res.json();
          if (error.code === 'token_expired') {
            await this._refresh();
            return this.request(path, options); // retry once
          }
          throw new Error(`${error.code}: ${error.message}`);
        }

        if (!res.ok) {
          const { error } = await res.json();
          throw new Error(`${error.code}: ${error.message}`);
        }

        if (res.status === 204) return null;
        return res.json();
      }

      async _refresh() {
        const res = await fetch('https://api.vendaze.com/oauth/token', {
          method: 'POST',
          headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
          body: new URLSearchParams({
            grant_type: 'refresh_token',
            refresh_token: this.refreshToken,
            client_id: this.clientId,
            client_secret: this.clientSecret,
          }),
        });

        if (!res.ok) throw new Error('Refresh token expired. User must re-authorize.');

        const tokens = await res.json();
        this.accessToken = tokens.access_token;
        this.refreshToken = tokens.refresh_token;

        if (this.onTokenRefresh) {
          await this.onTokenRefresh(tokens);
        }
      }
    }

    // Usage
    const client = new VendazeClient({
      accessToken: storedAccessToken,
      refreshToken: storedRefreshToken,
      clientId: process.env.VENDAZE_CLIENT_ID,
      clientSecret: process.env.VENDAZE_CLIENT_SECRET,
      onTokenRefresh: async (tokens) => {
        await db.updateTokens(workspaceSlug, tokens);
      },
    });

    const { data: people } = await client.request('/v1/people/PERSON_UUID');
    ```
  </Tab>
</Tabs>

***

## Python

Uses the `httpx` library (async). Install with `pip install httpx`.

<Tabs>
  <Tab title="OAuth token exchange">
    ```python theme={null}
    import httpx

    async def exchange_code(code: str, client_id: str, client_secret: str) -> dict:
        async with httpx.AsyncClient() as client:
            res = await client.post(
                "https://api.vendaze.com/oauth/token",
                data={
                    "grant_type": "authorization_code",
                    "code": code,
                    "client_id": client_id,
                    "client_secret": client_secret,
                },
            )

        res.raise_for_status()
        return res.json()
        # { "access_token": ..., "refresh_token": ..., "workspace_slug": ... }
    ```
  </Tab>

  <Tab title="Get a person by ID">
    ```python theme={null}
    import httpx

    async def get_person(access_token: str, person_id: str) -> dict:
        headers = {"Authorization": f"Bearer {access_token}"}

        async with httpx.AsyncClient() as client:
            res = await client.get(
                f"https://api.vendaze.com/v1/people/{person_id}",
                headers=headers,
            )
            res.raise_for_status()
            return res.json()["data"]
    ```
  </Tab>

  <Tab title="Create a person">
    ```python theme={null}
    import httpx
    import uuid

    async def create_person(access_token: str, payload: dict) -> dict:
        async with httpx.AsyncClient() as client:
            res = await client.post(
                "https://api.vendaze.com/v1/people",
                json=payload,
                headers={
                    "Authorization": f"Bearer {access_token}",
                    "Idempotency-Key": str(uuid.uuid4()),
                },
            )

        if res.status_code == 422:
            error = res.json()["error"]
            raise ValueError(f"Validation failed: {error['fields']}")

        res.raise_for_status()
        return res.json()["data"]

    # Usage
    person = await create_person(access_token, {
        "full_name": "Ana Costa",
        "emails": [{"email": "ana@company.com", "type": "work"}],
        "phones": [{"phone": "+5511999990001", "type": "mobile"}],
        "position": "Head of Sales",
    })
    ```
  </Tab>

  <Tab title="Error handling wrapper">
    ```python theme={null}
    import httpx
    import uuid
    from typing import Callable, Awaitable

    class VendazeClient:
        def __init__(
            self,
            access_token: str,
            refresh_token: str,
            client_id: str,
            client_secret: str,
            on_token_refresh: Callable[[dict], Awaitable[None]] | None = None,
        ):
            self.access_token = access_token
            self.refresh_token = refresh_token
            self.client_id = client_id
            self.client_secret = client_secret
            self.on_token_refresh = on_token_refresh

        async def request(self, method: str, path: str, **kwargs) -> dict | None:
            async with httpx.AsyncClient() as client:
                res = await client.request(
                    method,
                    f"https://api.vendaze.com{path}",
                    headers={"Authorization": f"Bearer {self.access_token}"},
                    **kwargs,
                )

            if res.status_code == 401:
                error = res.json()["error"]
                if error["code"] == "token_expired":
                    await self._refresh()
                    return await self.request(method, path, **kwargs)
                raise PermissionError(f"{error['code']}: {error['message']}")

            if res.status_code == 204:
                return None

            res.raise_for_status()
            return res.json()

        async def _refresh(self):
            async with httpx.AsyncClient() as client:
                res = await client.post(
                    "https://api.vendaze.com/oauth/token",
                    data={
                        "grant_type": "refresh_token",
                        "refresh_token": self.refresh_token,
                        "client_id": self.client_id,
                        "client_secret": self.client_secret,
                    },
                )

            if not res.is_success:
                raise PermissionError("Refresh token expired. User must re-authorize.")

            tokens = res.json()
            self.access_token = tokens["access_token"]
            self.refresh_token = tokens["refresh_token"]

            if self.on_token_refresh:
                await self.on_token_refresh(tokens)
    ```
  </Tab>
</Tabs>

***

## Go

Uses the standard library only. No external dependencies.

<Tabs>
  <Tab title="OAuth token exchange">
    ```go theme={null}
    package vendaze

    import (
        "encoding/json"
        "fmt"
        "net/http"
        "net/url"
        "strings"
    )

    type TokenResponse struct {
        AccessToken   string `json:"access_token"`
        RefreshToken  string `json:"refresh_token"`
        TokenType     string `json:"token_type"`
        ExpiresIn     int    `json:"expires_in"`
        WorkspaceSlug string `json:"workspace_slug"`
    }

    func ExchangeCode(code, clientID, clientSecret string) (*TokenResponse, error) {
        form := url.Values{
            "grant_type":    {"authorization_code"},
            "code":          {code},
            "client_id":     {clientID},
            "client_secret": {clientSecret},
        }

        res, err := http.Post(
            "https://api.vendaze.com/oauth/token",
            "application/x-www-form-urlencoded",
            strings.NewReader(form.Encode()),
        )
        if err != nil {
            return nil, err
        }
        defer res.Body.Close()

        if res.StatusCode != http.StatusOK {
            var errBody struct {
                Error struct {
                    Code    string `json:"code"`
                    Message string `json:"message"`
                } `json:"error"`
            }
            json.NewDecoder(res.Body).Decode(&errBody)
            return nil, fmt.Errorf("%s: %s", errBody.Error.Code, errBody.Error.Message)
        }

        var tokens TokenResponse
        if err := json.NewDecoder(res.Body).Decode(&tokens); err != nil {
            return nil, err
        }
        return &tokens, nil
    }
    ```
  </Tab>

  <Tab title="Get a person by ID">
    ```go theme={null}
    package vendaze

    import (
        "encoding/json"
        "fmt"
        "net/http"
    )

    type Person struct {
        ID        string `json:"id"`
        FullName  string `json:"full_name"`
        CreatedAt string `json:"created_at"`
    }

    func GetPerson(accessToken string, personID string) (*Person, error) {
        client := &http.Client{}

        req, _ := http.NewRequest("GET",
            fmt.Sprintf("https://api.vendaze.com/v1/people/%s", personID),
            nil,
        )
        req.Header.Set("Authorization", "Bearer "+accessToken)

        res, err := client.Do(req)
        if err != nil {
            return nil, err
        }
        defer res.Body.Close()

        if res.StatusCode != http.StatusOK {
            return nil, fmt.Errorf("unexpected status: %d", res.StatusCode)
        }

        var body struct {
            Data Person `json:"data"`
        }
        if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
            return nil, err
        }

        return &body.Data, nil
    }
    ```
  </Tab>

  <Tab title="Create a person">
    ```go theme={null}
    package vendaze

    import (
        "bytes"
        "encoding/json"
        "fmt"
        "net/http"

        "github.com/google/uuid"
    )

    type CreatePersonRequest struct {
        FullName string          `json:"full_name"`
        Emails   []ContactEmail  `json:"emails,omitempty"`
        Phones   []ContactPhone  `json:"phones,omitempty"`
        Position string          `json:"position,omitempty"`
    }

    type ContactEmail struct {
        Email string `json:"email"`
        Type  string `json:"type,omitempty"`
    }

    type ContactPhone struct {
        Phone string `json:"phone"`
        Type  string `json:"type,omitempty"`
    }

    func CreatePerson(accessToken string, payload CreatePersonRequest) (*Person, error) {
        body, _ := json.Marshal(payload)

        req, _ := http.NewRequest("POST", "https://api.vendaze.com/v1/people", bytes.NewReader(body))
        req.Header.Set("Authorization", "Bearer "+accessToken)
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set("Idempotency-Key", uuid.New().String())

        res, err := (&http.Client{}).Do(req)
        if err != nil {
            return nil, err
        }
        defer res.Body.Close()

        if res.StatusCode != http.StatusCreated {
            var errBody struct {
                Error struct {
                    Code    string            `json:"code"`
                    Message string            `json:"message"`
                    Fields  map[string]string `json:"fields,omitempty"`
                } `json:"error"`
            }
            json.NewDecoder(res.Body).Decode(&errBody)
            return nil, fmt.Errorf("%s: %s", errBody.Error.Code, errBody.Error.Message)
        }

        var result struct {
            Data Person `json:"data"`
        }
        if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
            return nil, err
        }
        return &result.Data, nil
    }
    ```
  </Tab>
</Tabs>

***

## Community resources

No official SDKs exist yet. If you build a client library for Vendaze, open a pull request on the [vendaze/vendaze-api](https://github.com/vendaze) repository to have it listed here.
