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.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.
All examples assume you have already completed the OAuth flow and have a valid
access_token and
refresh_token. See Authentication if you have not done that yet.Node.js
Uses the built-infetch API (Node.js 18+). No dependencies required.
- OAuth token exchange
- Refresh access token
- List people (paginated)
- Create a person
- Error handling wrapper
// 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 }
}
// 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();
}
// Fetch all people in a workspace, handling pagination automatically
async function listAllPeople(accessToken) {
const results = [];
let page = 1;
while (true) {
const res = await fetch(
`https://api.vendaze.com/v1/people?page=${page}&per_page=100&order_by=created_at&order=desc`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data, meta } = await res.json();
results.push(...data);
if (!meta.has_more) break;
page++;
}
return results;
}
// 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',
});
// 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?per_page=50');
Python
Uses thehttpx library (async). Install with pip install httpx.
- OAuth token exchange
- List people (paginated)
- Create a person
- Error handling wrapper
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": ... }
import httpx
async def list_all_people(access_token: str) -> list[dict]:
results = []
page = 1
headers = {"Authorization": f"Bearer {access_token}"}
async with httpx.AsyncClient() as client:
while True:
res = await client.get(
"https://api.vendaze.com/v1/people",
params={"page": page, "per_page": 100, "order_by": "created_at", "order": "desc"},
headers=headers,
)
res.raise_for_status()
body = res.json()
results.extend(body["data"])
if not body["meta"]["has_more"]:
break
page += 1
return results
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",
})
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)
Go
Uses the standard library only. No external dependencies.- OAuth token exchange
- List people (paginated)
- Create a person
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
}
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"`
}
type PaginationMeta struct {
Total int `json:"total"`
Page int `json:"page"`
PerPage int `json:"per_page"`
HasMore bool `json:"has_more"`
}
func ListAllPeople(accessToken string) ([]Person, error) {
var results []Person
page := 1
client := &http.Client{}
for {
req, _ := http.NewRequest("GET",
fmt.Sprintf("https://api.vendaze.com/v1/people?page=%d&per_page=100", page),
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"`
Meta PaginationMeta `json:"meta"`
}
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
return nil, err
}
results = append(results, body.Data...)
if !body.Meta.HasMore {
break
}
page++
}
return results, nil
}
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
}