Skip to main content

AtendeAqui SDK Python

SDK Python unificado para a plataforma AtendeAqui.

Instalação

pip install atendeaqui

Quick Start

from atendeaqui import AtendeAquiClient

client = AtendeAquiClient(api_token="seu-bearer-token")

# Onboarding
flow = client.onboarding.get_structure(flow_key="uuid-do-flow", language="pt-BR")
progress = client.onboarding.start_flow(flow_key="uuid-do-flow", user_id="user-123", name="João Silva", email="u@example.com")
progress = client.onboarding.complete_step(flow_key="uuid-do-flow", user_id="user-123", step_key="welcome")
print(f"Progresso: {progress.completion_percentage}%")

Autenticação

Com api_token (recomendado)

O api_token é um Bearer token gerado no dashboard. Dá acesso a todos os módulos: clientes, onboarding (admin e progresso) e futuros módulos.

client = AtendeAquiClient(api_token="seu-bearer-token")

# Clientes
client.clients.list()

# Onboarding — passa flow_key por operação
client.onboarding.start_flow(flow_key="uuid-do-flow", user_id="user-123")
client.onboarding.list_flows()
client.onboarding.get_analytics(slug="meu-flow")

Com flow_key default (atalho para um único flow)

Se o seu sistema interage com um único flow, passe flow_key na inicialização. Ele será usado como default em todos os métodos de onboarding (pode ser sobrescrito por chamada).

client = AtendeAquiClient(flow_key="f47ac10b-58cc-4372-a567-0e02b2c3d479")

# Usa o flow_key default — não precisa repetir
client.onboarding.get_structure()
client.onboarding.start_flow(user_id="user-123")

# Pode sobrescrever por chamada
client.onboarding.get_structure(flow_key="outro-flow-uuid")

Com ambos (API admin + flow default)

client = AtendeAquiClient(
    api_token="seu-bearer-token",
    flow_key="f47ac10b-58cc-4372-a567-0e02b2c3d479",
)
# Operações de progresso (usa flow_key default)
client.onboarding.start_flow(user_id="user-123")
# Operações admin (usa api_token)
client.onboarding.list_flows()
client.onboarding.get_analytics(slug="meu-flow")

Módulos

Clientes (requer api_token)

admin = AtendeAquiClient(api_token="seu-bearer-token")

# Listar clientes
clientes = admin.clients.list(page=1, page_size=50)

# Criar cliente (client_id é gerado automaticamente se não fornecido)
novo = admin.clients.create(
    full_name="Acme Corp",
    email="contato@acme.com",
    phone="1133334444",
    identification_number="12.345.678/0001-90",
    type_identification="CNPJ",
)
print(novo.client_id)  # UUID gerado automaticamente

# Ou criar com ID do seu sistema (evita manter mapeamento de IDs)
novo = admin.clients.create(
    full_name="Acme Corp",
    email="contato@acme.com",
    client_id="meu-sistema-123",  # opcional: usa o ID do seu sistema local
)

# Detalhes do cliente
cliente = admin.clients.get(client_id=novo.client_id)

# Atualizar cliente
admin.clients.update(novo.client_id, full_name="Acme Corp Ltda", phone="1199998888")

# Equipe do cliente
team = admin.clients.get_team(novo.client_id)
for member in team:
    print(f"{member.full_name} (primary={member.is_primary})")

# Adicionar membro (cria usuário se não existir)
member = admin.clients.add_team_member(
    client_id=novo.client_id,
    user_email="joao@acme.com",
    is_primary=True,
    can_create_tickets=True,
)

# Atualizar permissões
admin.clients.update_team_member(novo.client_id, member.id, can_edit_client=True)

# Remover membro
admin.clients.remove_team_member(novo.client_id, member.id)

Onboarding

FLOW = "uuid-do-flow"

# Estrutura do flow
flow = client.onboarding.get_structure(flow_key=FLOW, language="pt-BR")

# Iniciar onboarding
progress = client.onboarding.start_flow(
    flow_key=FLOW,
    user_id="user-123",
    name="João Silva",
    email="user@example.com",
    metadata={"company": "Acme", "preferred_language": "pt-BR"},
)

# Registrar início de um step (rastreamento, sem marcar como completo)
progress = client.onboarding.start_step(
    user_id="user-123",
    flow_key=FLOW,
    step_key="welcome",
    metadata={"source": "sidebar", "device": "desktop"},  # opcional
)

# Completar steps
progress = client.onboarding.complete_step(
    user_id="user-123",
    flow_key=FLOW,
    step_key="welcome",
    step_data={"accepted_terms": True},
)

# Completar múltiplos steps (batch)
progress = client.onboarding.complete_steps(
    user_id="user-123",
    flow_key=FLOW,
    steps=[
        {"step_key": "config", "step_data": {"api_key": "xxx"}},
        {"step_key": "test"},
    ],
)

# Pular step opcional
progress = client.onboarding.skip_step(user_id="user-123", flow_key=FLOW, step_key="tour")

# Atualizar metadata (merge)
progress = client.onboarding.update_metadata(
    user_id="user-123",
    flow_key=FLOW,
    metadata={"plan": "pro"},
)

# Consultar progresso
progress = client.onboarding.get_progress(user_id="user-123", flow_key=FLOW)
print(progress.status)              # IN_PROGRESS
print(progress.steps_completed)     # ['welcome', 'config']
print(progress.step_data)           # {'welcome': {'accepted_terms': True}, ...}
print(progress.completion_percentage)  # 75
print(progress.flow_slug)           # 'onboarding-clientes'

# Data real do evento (occurred_at): use quando enviar depois do fato
# (fila, reprocessamento, carga retroativa). Aceita datetime com fuso ou
# string ISO 8601; datetime sem fuso gera ValueError. Sem ele, vale o
# horário do servidor. Disponível em start_flow, start_step, complete_step,
# skip_step, update_metadata, complete_flow, restart_flow, abandon_flow e
# em cada item de complete_steps.
from datetime import datetime, timezone

progress = client.onboarding.complete_step(
    user_id="user-123",
    flow_key=FLOW,
    step_key="welcome",
    occurred_at=datetime(2026, 10, 20, 12, 30, tzinfo=timezone.utc),
)
progress = client.onboarding.complete_steps(
    user_id="user-123",
    flow_key=FLOW,
    steps=[
        {"step_key": "config", "occurred_at": "2026-10-20T09:30:00-03:00"},
        {"step_key": "test", "occurred_at": datetime.now(timezone.utc)},
    ],
)

# Ações no flow
client.onboarding.complete_flow(user_id="user-123", flow_key=FLOW)
client.onboarding.restart_flow(user_id="user-123", flow_key=FLOW)
client.onboarding.abandon_flow(user_id="user-123", flow_key=FLOW)

# Token para widget frontend
token = client.onboarding.get_widget_token(flow_key=FLOW, user_id="user-123", ttl=600)

# Admin: listar flows e analytics (requer api_token)
flows = client.onboarding.list_flows()
analytics = client.onboarding.get_analytics(slug="meu-flow")
progress_list = client.onboarding.list_progress(slug="meu-flow")

# Admin: acompanhamento automático do flow (requer api_token com escrita)
client.onboarding.update_flow(
    "meu-flow",
    step_stalled_after_hours=48,   # step iniciado e não concluído em 48 h = travado
    email_deadline_days=30,        # prazo desde o início
    auto_abandon_on_deadline=True, # marca ABANDONED quando o prazo vence
)

# Admin: e-mails automáticos de onboarding (requer api_token)
rules = client.onboarding.get_email_rules("meu-flow")
logs = client.onboarding.list_email_logs(
    "meu-flow", status="failed", event_type="flow_completed", created_after="2026-10-01",
)
summary = client.onboarding.get_email_logs_summary("meu-flow")
print(summary["total"], summary["by_event_type"])

# Admin: regras, peças de texto e descadastro (ver "E-mails de onboarding")
client.onboarding.create_email_rule("meu-flow", "step_stalled")
piece = client.onboarding.create_email_piece("meu-flow", "corpo", "Olá, {NOME}!", subject="Falta pouco")
client.onboarding.preview_email("meu-flow", "step_stalled", step_key="first-appointment")
client.onboarding.approve_email_piece("meu-flow", piece["id"])
client.onboarding.set_email_subscription("meu-flow", "user-123", unsubscribed=True)

Exemplos de Uso

Fluxo completo com dados de perfil

Os dados do metadata e do step_data alimentam os relatórios e os e-mails automáticos (variáveis como {DADO:company} e condições das peças; ver E-mails de onboarding).

from atendeaqui import AtendeAquiClient

client = AtendeAquiClient(flow_key="uuid-do-flow")

# 1. Iniciar o flow com metadata do usuário
#    Esses dados entram nos relatórios e nos e-mails (variáveis e condições das peças)
progress = client.onboarding.start_flow(
    user_id="empresa-456",
    name="João Silva",
    email="joao@agendapro.com.br",
    metadata={
        "company": "AgendaPro",
        "segment": "tecnologia",
        "plan_code": "professional",
        "preferred_language": "pt-BR",
    },
)
# >>> E-mail de boas-vindas é montado com as peças aprovadas (se houver regra para flow_started)

# 2. Completar etapa de perfil com caso de uso
#    step_data é JSON livre — envie tudo que descreve o cenário do cliente
progress = client.onboarding.complete_step(
    user_id="empresa-456",
    step_key="perfil",
    step_data={
        "uso_pretendido": "reuniões de vendas",
        "tamanho_equipe": "até 10 pessoas",
        "frequencia_uso": "diário",
        "integracao_desejada": "Google Calendar",
    },
)

# 3. Completar configuração técnica
progress = client.onboarding.complete_step(
    user_id="empresa-456",
    step_key="configuracao-api",
    step_data={
        "api_token": "EAAG...configurado",
        "provider": "google",
        "webhook_url": "https://agendapro.com.br/webhook",
    },
)

# 4. Completar múltiplos steps de uma vez
progress = client.onboarding.complete_steps(
    user_id="empresa-456",
    steps=[
        {
            "step_key": "convidar-equipe",
            "step_data": {
                "membros_convidados": 5,
                "emails": ["ana@agendapro.com.br", "pedro@agendapro.com.br"],
            },
        },
        {
            "step_key": "personalizar-agenda",
            "step_data": {
                "horario_inicio": "08:00",
                "horario_fim": "18:00",
                "duracao_padrao": "30min",
                "fuso_horario": "America/Sao_Paulo",
            },
        },
    ],
)

# 5. Completar o flow
client.onboarding.complete_flow(user_id="empresa-456")

Integração com sistema existente (SaaS)

Exemplo de integração server-side: criar cliente + iniciar onboarding vinculado.

from atendeaqui import AtendeAquiClient, NotFoundError

FLOW_KEY = "uuid-do-flow"

# api_token necessário para criar clientes
client = AtendeAquiClient(api_token="seu-bearer-token")


def on_client_signup(client_data: dict):
    """Chamado após o cadastro de um novo cliente no seu sistema."""

    # 1. Criar cliente no AtendeAqui usando o ID do seu sistema
    #    Isso evita manter um mapeamento separado de IDs
    novo_cliente = client.clients.create(
        full_name=client_data["company_name"],
        email=client_data["email"],
        client_id=client_data["id"],  # usa o ID do seu sistema local
    )

    # 2. Iniciar onboarding vinculado ao cliente
    progress = client.onboarding.start_flow(
        flow_key=FLOW_KEY,
        user_id=client_data["id"],
        name=client_data["name"],
        email=client_data["email"],
        metadata={
            "company": client_data.get("company_name", ""),
            "sector": client_data.get("industry", ""),
            "plan": client_data.get("plan", "free"),
        },
        client_id=novo_cliente.client_id,  # vincula onboarding ao cliente
    )

    return progress


def on_feature_configured(user_id: str, feature: str, config: dict):
    """Chamado quando o cliente configura uma feature no seu sistema."""

    step_map = {
        "api_setup": "configuracao-api",
        "team_invite": "convidar-equipe",
        "branding": "personalizar-marca",
    }

    step_key = step_map.get(feature)
    if not step_key:
        return

    try:
        progress = client.onboarding.complete_step(
            flow_key=FLOW_KEY,
            user_id=user_id,
            step_key=step_key,
            step_data=config,
        )
        return progress
    except NotFoundError:
        return None


def check_onboarding_status(user_id: str) -> dict:
    """Verifica o status do onboarding para exibir no dashboard."""

    try:
        progress = client.onboarding.get_progress(flow_key=FLOW_KEY, user_id=user_id)
        return {
            "status": progress.status,
            "percentage": progress.completion_percentage,
            "is_completed": progress.is_completed,
            "current_step": progress.current_step_title,
            "steps_done": progress.steps_completed,
        }
    except NotFoundError:
        return {"status": "NOT_STARTED", "percentage": 0}

Widget frontend com token JWT

from atendeaqui import AtendeAquiClient

FLOW_KEY = "uuid-do-flow"

# Server-side: gerar token seguro para o frontend
server_client = AtendeAquiClient(flow_key=FLOW_KEY)

# Em uma view/endpoint do seu backend:
def get_onboarding_token(request):
    token = server_client.onboarding.get_widget_token(
        user_id=request.user.external_id,
        client_id=request.user.client_id,
        ttl=600,  # 10 minutos
    )
    return {"token": token.token, "expires_in": token.expires_in}

Atualizar metadata em tempo real

Use update_metadata para enriquecer o perfil do cliente conforme ele interage com o sistema. Os e-mails seguintes já usam os valores novos (variáveis e condições das peças).

# Quando o cliente atualiza o plano
client.onboarding.update_metadata(
    flow_key=FLOW_KEY,
    user_id="empresa-456",
    metadata={"plan": "enterprise", "seats": 50},
)

# Quando detectar a timezone do cliente
client.onboarding.update_metadata(
    flow_key=FLOW_KEY,
    user_id="empresa-456",
    metadata={"timezone": "America/Sao_Paulo", "locale": "pt-BR"},
)

Admin: monitorar onboarding

# Requer api_token (Bearer token)
admin_client = AtendeAquiClient(api_token="seu-bearer-token")

# Listar todos os flows
flows = admin_client.onboarding.list_flows()
for flow in flows:
    print(f"{flow['name']}: {flow['total_starts']} inícios, "
          f"{flow['completion_rate']}% conclusão")

# Analytics de um flow específico
analytics = admin_client.onboarding.get_analytics(slug="onboarding-clientes")
print(f"Taxa de conclusão: {analytics.completion_rate}%")
print(f"Tempo médio: {analytics.average_completion_time}s")
print(f"Em progresso: {analytics.in_progress}")
print(f"Abandonados: {analytics.abandoned}")

# Drop-off por etapa
for step_key, data in analytics.step_drop_off.items():
    print(f"  {data['title']}: {data['completion_rate']}% "
          f"({data['dropped']} desistiram)")

# Listar progresso de todos os usuários
progress_list = admin_client.onboarding.list_progress(
    slug="onboarding-clientes",
    page=1,
    page_size=100,
)

E-mails de onboarding

Os e-mails não são gerados na hora: são montados a partir de peças de texto aprovadas e saem pelo provedor e domínio do seu produto. Nada é enviado sem aprovação.

1. Configurar o flow (uma vez)

client.onboarding.update_flow(
    "meu-flow",
    email_provider_config=12,             # provedor (ex.: Postmark do produto) cadastrado no AtendeAqui
    email_from_name="Minha Agenda",
    email_brand={
        "logo_url": "https://minhaagenda.com.br/static/email/logo.png",
        "color": "#1F6FEB",
        "signature": "Equipe Minha Agenda",
        "footer": "Minha Agenda · Rua Exemplo, 100 · Cidade/UF",   # obrigatório para envio real
    },
    email_blocks=["corpo", "etapa", "dica"],   # blocos do e-mail; "corpo" sempre existe
    email_dimensions=["segment"],              # metadados que podem variar o texto
)

2. Escrever e aprovar as peças

Cada e-mail tem o bloco corpo (assunto e texto) e os outros blocos do flow, que entram no corpo por {BLOCO:nome}. Cada peça vale para um evento, uma etapa e/ou valores de metadados; para cada bloco, vale a peça aprovada mais específica que combina com o usuário, e a genérica cobre o resto.

FLOW = "meu-flow"

client.onboarding.create_email_piece(
    FLOW, "corpo",
    "Olá, {NOME}!\n\nVocê parou em {ETAPA}.\n\n{BLOCO:etapa}\n\n{BLOCO:dica}",
    subject="{NOME}, falta pouco para abrir sua agenda",
    event_type="step_stalled",
    cta_label="Continuar",
)
client.onboarding.create_email_piece(FLOW, "etapa", "Crie um horário e mande o link para um cliente.",
                                     step_key="first-appointment")
client.onboarding.create_email_piece(FLOW, "dica", "Clínicas costumam liberar o agendamento pelo WhatsApp.",
                                     step_key="first-appointment", conditions={"segment": "saude"})

# Revisar como o e-mail sairia (inclui rascunhos)
preview = client.onboarding.preview_email(FLOW, "step_stalled", step_key="first-appointment",
                                          name="Bia", metadata={"segment": "saude"})
print(preview["subject"], preview["body_text"], preview["choices"])

for piece in client.onboarding.list_email_pieces(FLOW, status="draft"):
    client.onboarding.approve_email_piece(FLOW, piece["id"])

# O que falta: regras ativas sem um corpo genérico aprovado
print(client.onboarding.get_email_coverage(FLOW)["events_without_generic_body"])

Editar uma peça aprovada cria uma nova versão em rascunho; a aprovada continua valendo até a nova ser aprovada.

Variáveis: {NOME} (primeiro nome), {ETAPA} (título da etapa em foco), {LINK_ETAPA} (step_config.action_url da etapa), {PROGRESSO} ("2 de 5 etapas"), {DADO:chave} (qualquer metadado). Uma linha cuja variável fica vazia sai inteira do e-mail. O botão usa cta_label e o link da etapa; sem link, não há botão.

Etapa em foco por evento: flow_started, a primeira obrigatória; step_completed e reminder, a próxima obrigatória pendente; step_stalled, a etapa travada; flow_completed, nenhuma.

3. Ligar as regras

rule = client.onboarding.create_email_rule(FLOW, "step_stalled")    # começa em simulação
# ... confira os e-mails montados em list_email_logs / get_email_logs_summary ...
client.onboarding.update_email_rule(FLOW, rule["id"], send_mode="send")

Em simulação o e-mail é montado e fica no registro, sem ir para o destinatário.

Descadastro

Todo e-mail leva um link de descadastro (com confirmação) e o cabeçalho de descadastro em um clique do Gmail e do Yahoo. Para sincronizar a preferência que o usuário marcou no seu produto:

client.onboarding.set_email_subscription(FLOW, "user-123", unsubscribed=True)    # cancela e esvazia a fila
client.onboarding.set_email_subscription(FLOW, "user-123", unsubscribed=False)   # só com consentimento

Quando o usuário cancela pelo e-mail, os webhooks do flow recebem o evento email_unsubscribed, e o progresso traz email_unsubscribed=True.

Dados recomendados no start_flow

Campo Exemplo Uso
name "João Silva" {NOME} nos e-mails (não envie o e-mail no lugar do nome)
email "joao@acme.com" Destinatário
metadata.company "AgendaPro" {DADO:company}
metadata.segment "saude" Condição das peças, se estiver em email_dimensions
metadata.plan_code "trial" Idem
metadata.preferred_language "pt-BR" Idioma das peças (cai no idioma do flow se não houver)
metadata.is_test True Conta interna ou de teste: não recebe e-mail

Use valores fixos em minúsculas nos metadados e no step_data (ex.: "saude", não "Saúde"/"saude ").

Tratamento de Erros

from atendeaqui import (
    AtendeAquiClient,
    StepNotFoundError,
    NotFoundError,
    ValidationError,
    AtendeAquiError,
)

try:
    client.onboarding.complete_step(flow_key=FLOW_KEY, user_id="user-123", step_key="invalid")
except StepNotFoundError as e:
    print(f"Step não existe: {e.message}")
except NotFoundError as e:
    print(f"Não encontrado: {e.message}")
except ValidationError as e:
    print(f"Dados inválidos: {e.message}")
except AtendeAquiError as e:
    print(f"Erro [{e.code}]: {e.message}")

Hierarquia

AtendeAquiError
├── AuthenticationError    # 401/403
├── NotFoundError          # 404
├── ValidationError        # 400
│   ├── StepNotFoundError
│   └── StepNotSkippableError
├── OriginNotAllowedError  # CORS
├── RateLimitError         # 429
└── ServerError            # 5xx

Estrutura do Pacote

atendeaqui/
├── __init__.py          # AtendeAquiClient + exports
├── client.py            # Cliente principal
├── _http.py             # Transport HTTP compartilhado
├── exceptions.py        # Exceções compartilhadas
├── clients/             # Módulo de Clientes
│   ├── __init__.py      # ClientsModule
│   └── models.py        # Client, TeamMember
└── onboarding/          # Módulo de Onboarding
    ├── __init__.py      # OnboardingModule
    └── models.py        # FlowStructure, UserProgress, etc.

Novos módulos (tickets, knowledge_base, etc.) seguirão o mesmo padrão.

Requisitos

  • Python 3.10+
  • requests >= 2.28

Licença

MIT

Metadata

Release files for atendeaqui 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for atendeaqui 1.2.0
File Size Uploaded Provenance
atendeaqui-1.2.0.tar.gz 32.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for atendeaqui 1.2.0
File Interpreter ABI Platform
atendeaqui-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 56.9 kB

Release files / atendeaqui-1.2.0.tar.gz

Download URL atendeaqui-1.2.0.tar.gz
Size 32.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2bb0227f2e2a91439702a218b15c52cac1d1097390ccef6a9694398aeecf25f6
BLAKE2b-256 checksum
How to use checksums
70ef9a872a69ee995216aa9f479bfd9a22bf189cda946bf17d949ce194c47bbc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / atendeaqui-1.2.0-py3-none-any.whl

Download URL atendeaqui-1.2.0-py3-none-any.whl
Size 24.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9609ae7619a35ea5b33b9edb2288373bc25bdc71a2a4eba597cd07dd943fa807
BLAKE2b-256 checksum
How to use checksums
be8117cb84df3aaec4d5903cafe64bcddf7f2608c220e4bfad154b371623c346
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Security for atendeaqui 1.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page