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”.
-
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→ mostraomie_mode(mockoureal) +version. - Produção/live (Kamal):
-
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/jsonBUBBLE_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-Timestamp—Current date/time:extract UNIX× 1000. Backend tolera ±5min de drift.
-
Instalar API Connector. Plugin oficial “API Connector”. Add a new API com nome
Blueprintt × Omie Backend. Auth:Private key in header, Key nameAuthorization, Key valueBearer [API_KEY]([API_KEY]vira parâmetro do plugin). -
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.jsondeste site (https://blue-omie.fonsecagabriel.com.br/openapi.json). -
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"}
Sem workflows — backend descobre tudo sozinho
Seção intitulada “Sem workflows — backend descobre tudo sozinho”Versões anteriores pediam workflows “Pedido criado” + “Status pedido changed → confirmado”. Removidos.
Como o reconciler descobre o que precisa fazer:
| Mudança no Bubble | Como reconciler detecta | Tempo até NF / ação |
|---|---|---|
| Order criado | modified_date > cursor no próximo tick | até 60min |
Pagar.me confirmou pagamento (workflow Bubble legado muda status_pedido = confirmado) | shadow delta status_pedido | até 60min |
Operador cancela pedido (muda status_pedido = cancelado) | shadow delta status_pedido | até 60min |
Operador edita billing_scheduled_date_override (override dia 25) | shadow delta | até 60min |
Operador marca faturamento_manual = yes + preenche omie_nfse_numero_manual | shadow delta | até 60min |
| Urgência — operador clica “Forçar sync agora” | POST /v1/orders/{id}/sync-omie chama o mesmo handler do tick | imediato |
Botões de ops — backoffice
Seção intitulada “Botões de ops — backoffice”1 endpoint manual + algumas edições de campo no próprio Order Bubble. Sem workflows, sem múltiplos endpoints.
Botão único: “Forçar sync agora”
Seção intitulada “Botão único: “Forçar sync agora””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"}trigger ∈ auto (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:
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ósomie_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ção | Antes | Agora |
|---|---|---|
| Retentar NF após erro | POST /retry | Clica “Forçar sync agora” → /sync-omie (mesmo handler) |
| Cancelar NF | POST /cancel-nf | Edita Order.status_pedido = cancelado (+ motivo em campo livre). Reconciler detecta + executa CancelarNFSe no próximo tick (ou clica “Forçar sync”). |
| Marcar NF manual | POST /mark-nf-manual | Edita 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 25 | POST /override-day25 | Edita Order.billing_scheduled_date_override + Order.regra_dia25_override_motivo + (Bubble preenche) _by + _at. Reconciler reagenda run_billing_job. |
Writeback — campos escritos pelo backend
Seção intitulada “Writeback — campos escritos pelo backend”Backend faz PATCH no Bubble Data API atualizando estes campos. Você não escreve nestes — backend é dono. Bubble só lê pra exibir.
| Campo Bubble | Tipo | Valores possíveis | Quando atualiza |
|---|---|---|---|
status_pedido | text (Option Set ns_status_pedido) | aguardando_pagamento, confirmado, cancelado | Pagar.me webhook + ordem cancelada |
status_faturamento | text (Option Set ns_status_faturamento) | aguardando_pagamento, aguardando_confirmacao, bloqueado_dia25, agendado, faturando, nf_emitida, nf_cancelada, erro_faturamento, faturamento_manual | Cada transição do billing state machine |
omie_os_id | text | UUID Omie OS | Após CreateOS |
omie_nfse_id | text | UUID Omie NFS-e | Após EmitirNFSe |
numero_nfse | text | Número NFS-e Prefeitura | Após callback Omie |
link_nfse | text (URL) | PDF NFS-e | Após callback Omie |
Option Sets Bubble — criar antes
Seção intitulada “Option Sets Bubble — criar antes”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 mutaOrder.status_pedido = confirmadointernamente. Backend não recebe Pagar.me direto — descobre viamodified_date > cursorno próximo tick do reconciler.
Do ponto de vista Bubble, o ciclo está completo quando você observa:
- Bubble (workflow legado) mudou
Order.status_pedido→confirmadoapóscharge.paid. - Próximo tick reconciler (até 60min) detecta delta, executa
decide_billing+ Omie, patchaOmie Sync State. Omie Sync State.status_faturamentoevolui (aguardando_pagamento→agendado/bloqueado_dia25/nf_emitida).- Se urgência: clica “Forçar sync agora” pra disparar
/sync-omieimediato.
Testar local — apontar Bubble staging pro seu backend
Seção intitulada “Testar local — apontar Bubble staging pro seu backend”-
Rodar backend local.
cd backend && make dev(uvicorn em:8000). -
Expor via ngrok.
ngrok http 8000. Copy URLhttps://abc123.ngrok-free.app. -
Trocar base URL no API Connector. Substituir
https://blu-omie-api.fonsecagabriel.com.brpela sua URL ngrok. Re-initialize as calls que mudaram path. -
Disparar pedido de teste no Bubble staging. Use um backoffice pessoal pra não poluir tickets reais.
-
Observar logs. Terminal do
uvicornmostra request entrando + structlog estruturado por correlation id. DB local é sqlite tmp (auto-criado). -
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.
Reference rápido
Seção intitulada “Reference rápido”| Endpoint | Quando | Idempotency-Key sugerida |
|---|---|---|
POST /v1/orders/{id}/sync-omie | Botão “Forçar sync agora” (manual, urgência) | <order_id>:manual-sync:<timestamp> |
GET /v1/orders/{id}/status | Polling status (opcional) | n/a |
GET /health/live | Liveness (smoke teste base URL) | n/a |
GET /health/full | Config 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.
Fases de integração
Seção intitulada “Fases de integração”Roadmap progressivo em 3 fases verificáveis. Cada fase termina com proof verde objetiva — sem “deve estar funcionando”.
Princípio de testabilidade
Seção intitulada “Princípio de testabilidade”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.
Fase 0 — Engineering verification (pré-Bubble)
Seção intitulada “Fase 0 — Engineering verification (pré-Bubble)”Quem executa: backend engineer. Bubble envolvido: zero. Goal: provar que backend + Omie trial estão saudáveis ANTES de Bubble dev tocar qualquer coisa.
# 0.1 Health gatecurl 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/fullretornaomie_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 workflow | Tipo | Endpoint | Quando dispara |
|---|---|---|---|
BO-TestSeed | Backend Workflow API | POST /v1/test/seed-order | Botão “Criar teste” admin |
BO-Sync | Frontend workflow | POST /v1/orders/{id}/sync-omie | Botão “Sincronizar” |
BO-Verify | Frontend workflow | GET /v1/orders/{id}/status | Auto-refresh ou clique “Status” |
BO-ForceError | Backend Workflow | POST /v1/test/force-state (target=error) | Botão admin “Forçar erro” |
BO-DryRun | Frontend workflow | POST /v1/test/dry-run | Botão “Simular” |
BO-DeepHealth | Backend Workflow | GET /v1/test/health-deep | Cron Bubble hourly |
BO-Reset | Backend Workflow | POST /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/livevia Bubble retorna 200 - 1.3
BO-TestSeeddispara → retornabubble_order_idválido + record aparece no Bubble Data - 1.4
BO-Syncno order seeded → retorna 200 comomie_idsem <5s - 1.5
BO-Verifyno mesmo order → retorna{status: "synced"} - 1.6
BO-ForceError→ próximoBO-Verifyretorna{status: "error", reason: ...} - 1.7
BO-Resetscope=all→ próximoBO-TestSeedcria 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:
| ID | Cenário | Bubble action | Endpoint | Evidência Omie |
|---|---|---|---|---|
| A1 | Happy PJ | ”Sincronizar” em pedido PJ | sync-omie | OS faturada + NFS-e autorizada |
| A2 | Happy PF | Idem PF | sync-omie | OS faturada + NFS-e autorizada |
| A3 | Cancel sem NFS-e | ”Cancelar” antes de emitir | cancel-nf | OS status=cancelada |
| A4 | Cancel + reissue | ”Reemitir NFS-e” | cancel-nf + emit | Nota antiga cancelada + nova autorizada |
| A5 | Reduce participants | Editar quantidade | sync-omie | OS qty atualizada + valor recalculado |
| A6 | Cancel completo | ”Cancelar tudo” | cancel-nf + cancel-os | Ambos 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
Fase 3 — Prod cutover (deferred)
Seção intitulada “Fase 3 — Prod cutover (deferred)”Status: Pausado. Foco trial sandbox por ora.
Quando reativada:
- Confirmação Luiz: ativar AABC produtivo
- Swap
OMIE_APP_KEY/SECRETpra credenciais produtivas (ver Setup · Switch Omie) - Disable
ENABLE_TEST_API=YESem prod - Wire Bubble webhook real (hoje só trigger manual)
- 7 dias de observability antes de declarar GA
Gotchas + Troubleshooting
Seção intitulada “Gotchas + Troubleshooting”Erros que Bubble dev precisa tratar no workflow (não falhar UI):
| HTTP | Causa | O que Bubble faz |
|---|---|---|
401 Unauthorized | Bearer token errado/expirado | Mostra “Erro de auth — chame ops”. Confirme Authorization header no API Connector + key no plugin settings. |
409 Conflict | Idempotency-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 Error | Body 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 Early | Omie 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 Gateway | Omie/Prefeitura recusou (timeout, NFS-e rejeitada) | Mostra erro humano + botão Retry. Confluence 3812884488 lista códigos Omie. |
5xx outros | Backend down ou DB perdeu | Retry com backoff exponencial (1s/2s/4s/8s); se persistir, alertar ops. |
Timeout 30s — sync-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.