Pular para o conteúdo

Bubble Dev — Passo a passo

Você é o dev Bubble, vai conectar Blueprintt ao backend Blu × Omie. Backend é um reconciler autônomo — puxa Orders modificados via Data API a cada hora, decide e executa. Você não cria workflows — só o Omie Sync State Data Type, 3 Option Sets e 1 botão “Forçar sync”.

  1. Base URL.

    • Produção/live (Kamal): https://blu-omie-api.fonsecagabriel.com.br
    • Local dev (com ngrok): https://<your-tunnel>.ngrok-free.app

    Confirme com curl https://blu-omie-api.fonsecagabriel.com.br/health/full | jq → mostra omie_mode (mock ou real) + version.

  2. Headers obrigatórios em toda chamada Bubble → Backend.

    Authorization: Bearer <BUBBLE_TO_API_BEARER>
    Idempotency-Key: <uuid>
    X-Request-Timestamp: <epoch ms>
    Content-Type: application/json
    • BUBBLE_TO_API_BEARER é um static bearer compartilhado, rotação trimestral manual. Vive em Bubble Plugin Settings → API Connector (NÃO commitar, NÃO colar em workflow visível). Pra obter o valor atual: pedir pro time backend (backend/.env).
    • Idempotency-Key — gerar via expression Bubble (Current date/time:formatted as + suffix) ou plugin uuid. Garante que retry não duplica OS na Omie.
    • X-Request-TimestampCurrent date/time:extract UNIX × 1000. Backend tolera ±5min de drift.
  3. Instalar API Connector. Plugin oficial “API Connector”. Add a new API com nome Blueprintt × Omie Backend. Auth: Private key in header, Key name Authorization, Key value Bearer [API_KEY] ([API_KEY] vira parâmetro do plugin).

  4. Importar endpoints. Bubble não importa OpenAPI nativo — cada endpoint vira 1 API Call configurada manualmente. Abra a API (Try It) como referência (1 path × method = 1 API Call). OpenAPI schema servido em /openapi.json deste site (https://blue-omie.fonsecagabriel.com.br/openapi.json).

  5. Testar conectividade. Configure uma call GET /health/live. Clique Initialize call — deve retornar 200 com {"status":"ok"}. Se der 401, Bearer está errado. Se der CORS / timeout, base URL está errada.

    Terminal window
    curl -i https://blu-omie-api.fonsecagabriel.com.br/health/live
    # HTTP/2 200
    # {"status":"ok"}

Versões anteriores pediam workflows “Pedido criado” + “Status pedido changed → confirmado”. Removidos.

Como o reconciler descobre o que precisa fazer:

Mudança no BubbleComo reconciler detectaTempo até NF / ação
Order criadomodified_date > cursor no próximo tickaté 60min
Pagar.me confirmou pagamento (workflow Bubble legado muda status_pedido = confirmado)shadow delta status_pedidoaté 60min
Operador cancela pedido (muda status_pedido = cancelado)shadow delta status_pedidoaté 60min
Operador edita billing_scheduled_date_override (override dia 25)shadow deltaaté 60min
Operador marca faturamento_manual = yes + preenche omie_nfse_numero_manualshadow deltaaté 60min
Urgência — operador clica “Forçar sync agora”POST /v1/orders/{id}/sync-omie chama o mesmo handler do tickimediato

1 endpoint manual + algumas edições de campo no próprio Order Bubble. Sem workflows, sem múltiplos endpoints.

Quando usar. Qualquer urgência que não pode esperar o próximo tick de 1h. NF falhou (rate-limit Omie, REDUNDANT, erro Prefeitura), Pagar.me acabou de confirmar e operador precisa NF agora, operador acabou de editar data de override e quer ver propagar.

Endpoint. POST /v1/orders/{bubble_order_id}/sync-omie

Body (schema SyncOmieBody):

{
"trigger": "manual",
"actor": "ops:luiz@blueprintt.co"
}

triggerauto (legado — reconciler) | manual (botão UI). actor = identidade pra auditoria.

Headers. Idempotency-Key: <order_id>:manual-sync:<Current date/time:formatted as UNIX> — cada clique tem key única (timestamp) pra permitir múltiplas tentativas. Use key fixa (<order_id>:manual-sync) se quiser idempotência por order.

Curl:

Terminal window
curl -X POST https://blu-omie-api.fonsecagabriel.com.br/v1/orders/ORD-XYZ/sync-omie \
-H "Authorization: Bearer $BUBBLE_TO_API_BEARER" \
-H "Idempotency-Key: ORD-XYZ:manual-sync:$(date +%s)" \
-H "Content-Type: application/json" \
-d '{"trigger":"manual","actor":"ops:luiz@blueprintt.co"}'

Sucesso. 200 — reconciler executou para 1 order (mesmo handler do tick automático). Omie Sync State patchado via Data API. Bubble UI re-lê o Omie Sync State.

Falha.

  • 425 — Omie em ban 31min. Backend retenta sozinho no próximo tick após omie_ban_expires_at. Não trate como erro UI.
  • 5xx — backend falhou; reconciler reprocessa no próximo tick. UI pode mostrar “Tentando…”.

Outras ações operador — edita campo no Order Bubble

Seção intitulada “Outras ações operador — edita campo no Order Bubble”

Não precisa endpoint dedicado. Reconciler observa via modified_date.

AçãoAntesAgora
Retentar NF após erroPOST /retryClica “Forçar sync agora” → /sync-omie (mesmo handler)
Cancelar NFPOST /cancel-nfEdita Order.status_pedido = cancelado (+ motivo em campo livre). Reconciler detecta + executa CancelarNFSe no próximo tick (ou clica “Forçar sync”).
Marcar NF manualPOST /mark-nf-manualEdita Order.faturamento_manual = yes + Order.omie_nfse_numero_manual + Order.omie_nfse_codigo_manual. Reconciler reflete em Omie Sync State no próximo tick.
Override dia 25POST /override-day25Edita Order.billing_scheduled_date_override + Order.regra_dia25_override_motivo + (Bubble preenche) _by + _at. Reconciler reagenda run_billing_job.

Backend faz PATCH no Bubble Data API atualizando estes campos. Você não escreve nestes — backend é dono. Bubble só lê pra exibir.

Campo BubbleTipoValores possíveisQuando atualiza
status_pedidotext (Option Set ns_status_pedido)aguardando_pagamento, confirmado, canceladoPagar.me webhook + ordem cancelada
status_faturamentotext (Option Set ns_status_faturamento)aguardando_pagamento, aguardando_confirmacao, bloqueado_dia25, agendado, faturando, nf_emitida, nf_cancelada, erro_faturamento, faturamento_manualCada transição do billing state machine
omie_os_idtextUUID Omie OSApós CreateOS
omie_nfse_idtextUUID Omie NFS-eApós EmitirNFSe
numero_nfsetextNúmero NFS-e PrefeituraApós callback Omie
link_nfsetext (URL)PDF NFS-eApós callback Omie

Crie estes dois Option Sets exatamente com estes valores antes de configurar os campos:

ns_status_pedido (3 valores):

aguardando_pagamentoconfirmadocancelado

ns_status_faturamento (9 valores):

aguardando_pagamentoaguardando_confirmacaobloqueado_dia25agendadofaturandonf_emitidanf_canceladaerro_faturamentofaturamento_manual

Strings devem casar exatamente (lowercase, underscore) — backend escreve essa forma literal.

Pagar.me webhook — Bubble continua dono (legado)

Seção intitulada “Pagar.me webhook — Bubble continua dono (legado)”

Pagar.me charge.* events continuam no Bubble (workflow legado HMAC + idempotência funcionando). Bubble muta Order.status_pedido = confirmado internamente. Backend não recebe Pagar.me direto — descobre via modified_date > cursor no próximo tick do reconciler.

Do ponto de vista Bubble, o ciclo está completo quando você observa:

  • Bubble (workflow legado) mudou Order.status_pedidoconfirmado após charge.paid.
  • Próximo tick reconciler (até 60min) detecta delta, executa decide_billing + Omie, patcha Omie Sync State.
  • Omie Sync State.status_faturamento evolui (aguardando_pagamentoagendado / bloqueado_dia25 / nf_emitida).
  • Se urgência: clica “Forçar sync agora” pra disparar /sync-omie imediato.

Testar local — apontar Bubble staging pro seu backend

Seção intitulada “Testar local — apontar Bubble staging pro seu backend”
  1. Rodar backend local. cd backend && make dev (uvicorn em :8000).

  2. Expor via ngrok. ngrok http 8000. Copy URL https://abc123.ngrok-free.app.

  3. Trocar base URL no API Connector. Substituir https://blu-omie-api.fonsecagabriel.com.br pela sua URL ngrok. Re-initialize as calls que mudaram path.

  4. Disparar pedido de teste no Bubble staging. Use um backoffice pessoal pra não poluir tickets reais.

  5. Observar logs. Terminal do uvicorn mostra request entrando + structlog estruturado por correlation id. DB local é sqlite tmp (auto-criado).

  6. Voltar pra live. Após terminar, trocar base URL de volta pra https://blu-omie-api.fonsecagabriel.com.br. Não esqueça — clientes reais ficarão tentando hit no seu laptop.

EndpointQuandoIdempotency-Key sugerida
POST /v1/orders/{id}/sync-omieBotão “Forçar sync agora” (manual, urgência)<order_id>:manual-sync:<timestamp>
GET /v1/orders/{id}/statusPolling status (opcional)n/a
GET /health/liveLiveness (smoke teste base URL)n/a
GET /health/fullConfig no ar (omie_mode + version)n/a

retry, cancel-nf, mark-nf-manual, payment-confirmed, override-day25, approve-billing-hold, transfer-cc, use-cc não existem mais (ou nunca existiram). Substituídos por edição de campo no Order Bubble + reconciler — ver Sem workflows.

Roadmap progressivo em 3 fases verificáveis. Cada fase termina com proof verde objetiva — sem “deve estar funcionando”.

Bubble dev usa endpoints /v1/test/* (gated por ENABLE_TEST_API=YES) pra criar cenários, forçar estados, simular eventos e resetar. Sem mexer dados reais. Detalhe em Testability.

Quem executa: backend engineer. Bubble envolvido: zero. Goal: provar que backend + Omie trial estão saudáveis ANTES de Bubble dev tocar qualquer coisa.

Terminal window
# 0.1 Health gate
curl https://blu-omie-api.fonsecagabriel.com.br/health/full | jq
# 0.2 Suite local (zero Omie real)
cd backend && uv run pytest tests/ -m "not integration" -q
# espera: 93 passed
# 0.3 Smoke integration (live Omie sandbox)
cd backend && RUN_INTEGRATION=1 uv run pytest tests/integration/ -v
# espera: A1 + A2 verdes (A4/A6 podem skip se Prefeitura sem homologação)

Proof verde:

  • GET /health/full retorna omie_mode + version
  • 93 testes locais passam
  • A1 + A2 integration verdes (live)
  • Print do ERP Omie mostra OS criadas pelos testes

Fase 1 — Bubble plug-in setup (zero dados reais)

Seção intitulada “Fase 1 — Bubble plug-in setup (zero dados reais)”

Quem executa: Bubble dev. Backend envolvido: endpoints /v1/test/*. Goal: Bubble consegue chamar API, workflows básicos funcionam, dados todos teste.

7 workflows nomeados:

Nome workflowTipoEndpointQuando dispara
BO-TestSeedBackend Workflow APIPOST /v1/test/seed-orderBotão “Criar teste” admin
BO-SyncFrontend workflowPOST /v1/orders/{id}/sync-omieBotão “Sincronizar”
BO-VerifyFrontend workflowGET /v1/orders/{id}/statusAuto-refresh ou clique “Status”
BO-ForceErrorBackend WorkflowPOST /v1/test/force-state (target=error)Botão admin “Forçar erro”
BO-DryRunFrontend workflowPOST /v1/test/dry-runBotão “Simular”
BO-DeepHealthBackend WorkflowGET /v1/test/health-deepCron Bubble hourly
BO-ResetBackend WorkflowPOST /v1/test/reset (scope=all)Botão admin “Reset sandbox”

Checklist:

  • 1.1 API Connector configurado manualmente (1 API Call por endpoint — Bubble não importa OpenAPI nativo)
  • 1.2 Header auth configurado → GET /health/live via Bubble retorna 200
  • 1.3 BO-TestSeed dispara → retorna bubble_order_id válido + record aparece no Bubble Data
  • 1.4 BO-Sync no order seeded → retorna 200 com omie_ids em <5s
  • 1.5 BO-Verify no mesmo order → retorna {status: "synced"}
  • 1.6 BO-ForceError → próximo BO-Verify retorna {status: "error", reason: ...}
  • 1.7 BO-Reset scope=all → próximo BO-TestSeed cria order com novo id

Proof verde: screen “Sandbox Console” com 7 botões. Operador clica em sequência, cada workflow retorna 200. Screenshot é o entregável.

Fase 2 — Sandbox smoke (Omie trial real, Bubble flows reais)

Seção intitulada “Fase 2 — Sandbox smoke (Omie trial real, Bubble flows reais)”

Quem executa: Bubble dev + PM. Goal: pedido real Bubble → OS/NFS-e real no Omie trial. Validação visual ponta-a-ponta.

Pré-requisitos: Fase 0 e 1 verdes · ENABLE_TEST_API ainda ativo (resets) · Cidade do evento tem homologação NFS-e OU plano é só testar A1/A2/A3/A5 (sem NFS-e).

6 cenários — 1 click Bubble + 1 screenshot Omie ERP:

IDCenárioBubble actionEndpointEvidência Omie
A1Happy PJ”Sincronizar” em pedido PJsync-omieOS faturada + NFS-e autorizada
A2Happy PFIdem PFsync-omieOS faturada + NFS-e autorizada
A3Cancel sem NFS-e”Cancelar” antes de emitircancel-nfOS status=cancelada
A4Cancel + reissue”Reemitir NFS-e”cancel-nf + emitNota antiga cancelada + nova autorizada
A5Reduce participantsEditar quantidadesync-omieOS qty atualizada + valor recalculado
A6Cancel completo”Cancelar tudo”cancel-nf + cancel-osAmbos cancelados

Reliability gate: 10 hourly runs sem ban Omie. POST seed-order + POST sync-omie em loop 1h × 10. Esperar 10/10 200, 0 bans, 0 park events.

Proof verde:

  • 6 PNGs golden em docs/test-reports/screenshots/golden/
  • 10 hourly runs sem ban
  • Dashboard reflete numbers da Omie
  • Sentry sem erros últimas 24h

Status: Pausado. Foco trial sandbox por ora.

Quando reativada:

  • Confirmação Luiz: ativar AABC produtivo
  • Swap OMIE_APP_KEY/SECRET pra credenciais produtivas (ver Setup · Switch Omie)
  • Disable ENABLE_TEST_API=YES em prod
  • Wire Bubble webhook real (hoje só trigger manual)
  • 7 dias de observability antes de declarar GA

Erros que Bubble dev precisa tratar no workflow (não falhar UI):

HTTPCausaO que Bubble faz
401 UnauthorizedBearer token errado/expiradoMostra “Erro de auth — chame ops”. Confirme Authorization header no API Connector + key no plugin settings.
409 ConflictIdempotency-Key já em uso ou estado inválido (ex.: NF já cancelada)NÃO retry com mesma key. Use timestamp/UUID nova pra ações repetíveis (retry). Pra cancel-nf/sync-omie key fixa é correto — significa “já foi feito”.
422 Validation ErrorBody malformado (campo obrigatório ausente / tipo errado)Bug do Bubble call. Log payload + raise — não deveria acontecer em prod. Detalhe em response.body.detail.
425 Too EarlyOmie ban 31min (rate-limit cold)Backend retry automático em background. Não tratar como erro UI. Mostre spinner / “Sincronizando…” até status_faturamento mudar. NUNCA retry agressivo (extende o ban).
429 Rate limit (REDUNDANT)Omie cache por ID (60s)Esperar ≥65s + retry. Backend já trata internamente.
502 Bad GatewayOmie/Prefeitura recusou (timeout, NFS-e rejeitada)Mostra erro humano + botão Retry. Confluence 3812884488 lista códigos Omie.
5xx outrosBackend down ou DB perdeuRetry com backoff exponencial (1s/2s/4s/8s); se persistir, alertar ops.

Timeout 30ssync-omie síncrono normal leva ~3s; >10s indica problema. Cancela a call e mude pra polling em GET /v1/orders/{id}/status.

Webhook Pagar.me continua no Bubble (legado, esperado) — workflow Bubble valida HMAC + idempotência e muda Order.status_pedido = confirmado. Reconciler detecta via modified_date > cursor no próximo tick. Manter intacto.