Tá RevisadoRevisado..Voltar para o site

Android Integration Guide

Integração Android com a API Tá Revisado

Guia didático para autenticar com Firebase e consumir o backend Cloud Run com tudo que precisa ser enviado: método, URL, headers, path params, body e respostas esperadas.

Base URL

https://cardocs-backend-5qq5b33fha-rj.a.run.app

Auth

Firebase ID token no header

Endpoints

19 rotas documentadas

Visão geral

Como pensar na integração

1

Autentique no Firebase

O Android faz login com Firebase Auth e envia o ID token no header Authorization.

2

Use o backend como fonte de verdade

Não grave direto no Firestore. Todas as mudanças passam pelas rotas `/v1`.

3

Recarregue o dashboard

Depois de salvar nota, documento, veículo ou transferência, atualize `/v1/dashboard`.

Setup Android

Dependências e cliente HTTP

O exemplo abaixo usa Firebase Auth, OkHttp, Retrofit e Moshi. Você pode trocar Retrofit por Ktor, mas mantenha o mesmo contrato: `Authorization: Bearer`, `Accept: application/json` e `Content-Type: application/json` quando houver body.

Gradle
// build.gradle.kts (Module: app)
dependencies {
    implementation(platform("com.google.firebase:firebase-bom:34.6.0"))
    implementation("com.google.firebase:firebase-auth")

    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
    implementation("com.squareup.retrofit2:retrofit:2.11.0")
    implementation("com.squareup.retrofit2:converter-moshi:2.11.0")
    implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
}
Token provider
class FirebaseTokenProvider {
    suspend fun idToken(forceRefresh: Boolean = false): String {
        val user = FirebaseAuth.getInstance().currentUser
            ?: error("Usuário não autenticado")

        return user.getIdToken(forceRefresh).await().token
            ?: error("Firebase não retornou ID token")
    }
}
OkHttp
class AuthInterceptor(
    private val tokenProvider: FirebaseTokenProvider
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val token = runBlocking { tokenProvider.idToken() }
        val request = chain.request().newBuilder()
            .header("Authorization", "Bearer $token")
            .header("Accept", "application/json")
            .header("Content-Type", "application/json")
            .build()

        return chain.proceed(request)
    }
}

val okHttp = OkHttpClient.Builder()
    .addInterceptor(AuthInterceptor(FirebaseTokenProvider()))
    .addInterceptor(HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BASIC
    })
    .build()
Retrofit service
interface TaRevisadoApi {
    @GET("v1/dashboard")
    suspend fun dashboard(): VehicleDashboardDto

    @POST("v1/vehicles/plate-lookup")
    suspend fun lookupPlate(@Body body: PlateLookupRequest): VehicleCandidateDto

    @POST("v1/vehicles")
    suspend fun createVehicle(@Body body: VehicleRegistrationRequest): VehicleProfileDto

    @POST("v1/invoices/analyze")
    suspend fun analyzeInvoice(@Body body: InvoiceDocumentInput): InvoiceScanDraftDto

    @POST("v1/invoices")
    suspend fun saveInvoice(@Body body: SaveInvoiceRequest): AutomationResultDto

    @POST("v1/vehicle-transfers")
    suspend fun requestTransfer(@Body body: CreateVehicleTransferRequest): VehicleTransferDto
}

val api = Retrofit.Builder()
    .baseUrl("https://cardocs-backend-5qq5b33fha-rj.a.run.app/")
    .client(okHttp)
    .addConverterFactory(MoshiConverterFactory.create())
    .build()
    .create(TaRevisadoApi::class.java)

Autenticação

Todo endpoint `/v1` protegido exige Firebase ID token

Depois do login no Firebase Auth, gere um ID token e envie no header. Se receber `401`, faça refresh forçado do token e repita a chamada uma única vez.

Headers padrão

  • Authorization: Bearer <firebase-id-token>
  • Accept: application/json
  • Content-Type: application/json
Repository pattern
class TaRevisadoRepository(private val api: TaRevisadoApi) {
    suspend fun loadGarage(): Result<VehicleDashboardDto> = runCatching {
        api.dashboard()
    }

    suspend fun registerVehicle(plate: String, mileage: Int): Result<VehicleProfileDto> = runCatching {
        val candidate = api.lookupPlate(PlateLookupRequest(plate))
        api.createVehicle(
            VehicleRegistrationRequest(
                plate = candidate.plate,
                initialMileage = mileage
            )
        )
    }
}

Referência de endpoints

Rotas disponíveis

Cada rota mostra exatamente o que enviar: método e URL completa, headers, path params quando existirem, body JSON e resposta representativa. Campos adicionais podem aparecer conforme o histórico do veículo cresce.

Endpoints públicos

Rotas sem autenticação. Use para health check, relatórios públicos e links compartilháveis.

GET/v1/healthPúblico

Health check

Confirma se o runtime Node/Cloud Run está respondendo.

URL e método
GET https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/health
Headers enviados
Accept: application/json
Body enviado
Sem body.
Response
{
  "status": "UP",
  "runtime": "node"
}
GET/v1/public/reports/{slug}Público

Relatório público em JSON

Busca o dossiê público de revenda gerado para um veículo.

URL e método
GET https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/public/reports/{slug}
Headers enviados
Accept: application/json
Path params
slug: substitua no path antes de chamar.
Body enviado
Sem body.
Response
{
  "title": "Dossiê Tá Revisado",
  "summary": "Histórico consolidado com manutenções...",
  "score": 82,
  "estimatedValueIncrease": 1250.5,
  "publicReportURL": "https://cardocs-backend-5qq5b33fha-rj.a.run.app/r/ABC1D23-2F8A91B0",
  "highlights": [],
  "checks": [],
  "reportSections": []
}
GET/r/{slug}Público

Relatório público em HTML

Renderiza uma página pública para compartilhar com comprador, anúncio ou vendedor.

URL e método
GET https://cardocs-backend-5qq5b33fha-rj.a.run.app/r/{slug}
Headers enviados
Accept: text/html
Path params
slug: substitua no path antes de chamar.
Body enviado
Sem body.
Response
HTTP 200
Content-Type: text/html; charset=utf-8

Sessão e conta

Rotas que ligam o usuário Firebase à conta Tá Revisado e mantêm o dispositivo Android sincronizado.

POST/v1/meBearer Firebase ID token

Criar ou atualizar perfil

Sincroniza o usuário autenticado no Firebase Auth com o backend Tá Revisado

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/me
Headers enviados
Authorization: Bearer <firebase-id-token>
Accept: application/json
Body enviado
Sem body.
Response
{
  "id": "firebase-uid",
  "email": "cliente@email.com",
  "displayName": "Cliente Tá Revisado",
  "photoURL": null,
  "emailVerified": true,
  "signInProvider": "password",
  "providerIds": ["password"]
}
DELETE/v1/meBearer Firebase ID token

Excluir conta e dados

Remove dados do usuário no Firestore/Storage antes de excluir a conta no Firebase Auth.

  • Chame esta rota antes de limpar sessão local.
  • Não tente apagar dados diretamente pelo client Firestore.
URL e método
DELETE https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/me
Headers enviados
Authorization: Bearer <firebase-id-token>
Accept: application/json
Body enviado
Sem body.
Response
HTTP 204 No Content
POST/v1/device-tokensBearer Firebase ID token

Registrar token push

Registra o token FCM do aparelho para alertas de transferência de veículo.

  • A API hoje aceita o valor de plataforma `ios`; para Android, combine o contrato antes de enviar `android`.
URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/device-tokens
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "token": "fcm-token-do-aparelho",
  "platform": "ios"
}
Response
HTTP 204 No Content
POST/v1/device-tokens/removeBearer Firebase ID token

Remover token push

Remove o token FCM no logout, troca de conta ou revogação de permissão.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/device-tokens/remove
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "token": "fcm-token-do-aparelho"
}
Response
HTTP 204 No Content

Garagem e veículo

Fluxo principal para carregar dashboard, consultar placa real e cadastrar veículo na garagem.

GET/v1/dashboardBearer Firebase ID token

Dashboard do usuário

Retorna garagem, veículo selecionado, documentos, histórico, dossiê e transferências pendentes.

URL e método
GET https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/dashboard
Headers enviados
Authorization: Bearer <firebase-id-token>
Accept: application/json
Body enviado
Sem body.
Response
{
  "id": "dashboard-id",
  "garages": [],
  "selectedGarageID": "00000000-0000-5000-8000-000000000000",
  "detectedVehicle": {
    "id": "00000000-0000-5000-8000-000000000000",
    "kind": "car",
    "plate": "",
    "brand": "",
    "model": "",
    "year": "",
    "color": "",
    "image": null,
    "fipe": null,
    "details": null
  },
  "incomingVehicleTransfers": [],
  "outgoingVehicleTransfers": []
}
POST/v1/vehicles/plate-lookupBearer Firebase ID token

Consultar placa

Busca dados reais do veículo por placa antes de permitir o cadastro.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/vehicles/plate-lookup
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "plate": "ABC1D23"
}
Response
{
  "id": "candidate-id",
  "kind": "car",
  "plate": "ABC1D23",
  "brand": "Toyota",
  "model": "Corolla",
  "year": "2022",
  "color": "Prata",
  "image": null,
  "fipe": null,
  "details": null
}
POST/v1/vehiclesBearer Firebase ID token

Cadastrar veículo

Cadastra o veículo depois de revalidar a placa no backend.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/vehicles
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "plate": "ABC1D23",
  "initialMileage": 42000
}
Response
{
  "id": "vehicle-id",
  "kind": "car",
  "plate": "ABC1D23",
  "maskedPlate": "ABC1D2*",
  "brand": "Toyota",
  "model": "Corolla",
  "year": "2022",
  "color": "Prata",
  "mileage": 42000,
  "nextServiceTitle": "Primeira organizacao",
  "nextServiceDistance": "Pronto para importar historico",
  "statusTags": ["Placa Verificada"]
}
POST/v1/vehicles/imageBearer Firebase ID token

Buscar imagem do veículo

Consulta imagem do veículo por marca, modelo e ano.

  • Pode retornar HTTP 404 quando não houver imagem disponível.
URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/vehicles/image
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "brand": "Toyota",
  "model": "Corolla",
  "year": "2022"
}
Response
{
  "url": "https://...",
  "thumbnailUrl": "https://...",
  "mime": "image/jpeg",
  "width": 1024,
  "height": 768,
  "accentColor": "#8A8889",
  "source": "carsxe"
}

Notas fiscais e documentos

Rotas para análise de documento, persistência no histórico e cofre digital do veículo.

POST/v1/invoices/analyzeBearer Firebase ID token

Analisar nota fiscal

Extrai dados estruturados a partir de OCR ou arquivo enviado em base64.

  • Envie `ocrText` com no mínimo 16 caracteres ou `document.base64Data`.
  • Arquivos aceitos: PDF, JPEG, PNG, TIFF, GIF, BMP e WebP.
URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/invoices/analyze
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "source": "cameraScan",
  "displayName": "nota-oficina.pdf",
  "ocrText": "Texto extraído no Android...",
  "pageCount": 1,
  "document": {
    "mimeType": "application/pdf",
    "base64Data": "JVBERi0xLjQ..."
  }
}
Response
{
  "id": "draft-id",
  "source": "cameraScan",
  "supplierName": "Oficina Central",
  "serviceTitle": "Troca de óleo",
  "category": "Manutenção",
  "date": "2026-05-12",
  "amount": 389.9,
  "mileage": 42310,
  "confidence": 91,
  "lineItems": [],
  "extractedFields": [],
  "healthImpacts": []
}
POST/v1/invoicesBearer Firebase ID token

Salvar nota no histórico

Transforma um draft analisado em registro de manutenção e documento no cofre.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/invoices
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "vehicleID": "vehicle-id",
  "draft": {
    "id": "draft-id",
    "source": "cameraScan",
    "supplierName": "Oficina Central",
    "serviceTitle": "Troca de óleo",
    "category": "Manutenção",
    "date": "2026-05-12",
    "amount": 389.9,
    "mileage": 42310,
    "confidence": 91,
    "lineItems": [],
    "extractedFields": [],
    "healthImpacts": []
  },
  "sourceDocument": null
}
Response
{
  "title": "Troca de óleo registrada",
  "message": "Histórico atualizado.",
  "investmentDelta": {
    "total": 389.9,
    "maintenance": 389.9,
    "documentsAndTaxes": 0
  },
  "record": {},
  "document": {}
}
POST/v1/documentsBearer Firebase ID token

Adicionar documento ao cofre

Salva documento do veículo, como CRLV, IPVA, recibos e comprovantes.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/documents
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "vehicleID": "vehicle-id",
  "title": "CRLV 2026",
  "documentType": "CRLV",
  "date": "2026",
  "notes": "Documento atualizado",
  "sourceDocument": {
    "source": "fileImport",
    "displayName": "crlv.pdf",
    "pageCount": 1,
    "document": {
      "mimeType": "application/pdf",
      "base64Data": "JVBERi0xLjQ..."
    }
  }
}
Response
{
  "id": "document-id",
  "title": "CRLV 2026",
  "status": "Anexado",
  "kind": "vehicleDocument",
  "documentType": "CRLV",
  "attachment": {
    "storagePath": "users/.../crlv.pdf",
    "downloadURL": "https://...",
    "mimeType": "application/pdf",
    "fileName": "crlv.pdf",
    "sizeBytes": 120000,
    "pageCount": 1,
    "source": "fileImport"
  }
}
POST/v1/documents/updateBearer Firebase ID token

Editar documento

Atualiza metadados editáveis de um documento salvo.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/documents/update
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "vehicleID": "vehicle-id",
  "documentID": "document-id",
  "title": "CRLV atualizado",
  "date": "2026",
  "documentType": "CRLV",
  "notes": "Emitido no app oficial"
}
Response
{ ...vaultDocument }
POST/v1/maintenance-records/updateBearer Firebase ID token

Editar manutenção

Atualiza título, subtítulo, data, valor e resumo de uma manutenção.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/maintenance-records/update
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "vehicleID": "vehicle-id",
  "recordID": "record-id",
  "title": "Revisão 40 mil km",
  "subtitle": "Óleo, filtros e inspeção",
  "date": "2026-05-12",
  "amount": 720,
  "supplierName": "Oficina Central",
  "serviceTitle": "Revisão completa",
  "purchaseSummary": "Óleo, filtros e mão de obra"
}
Response
{ ...maintenanceRecord }

Dossiê e transferência

Rotas para gerar relatório de revenda e mover um veículo para outro usuário Tá Revisado

POST/v1/resale-dossiersBearer Firebase ID token

Gerar dossiê de revenda

Gera ou atualiza o relatório público do veículo.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/resale-dossiers
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "vehicleID": "vehicle-id"
}
Response
{
  "title": "Dossiê Tá Revisado",
  "summary": "Histórico consolidado...",
  "score": 82,
  "estimatedValueIncrease": 1250.5,
  "publicReportURL": "https://cardocs-backend-5qq5b33fha-rj.a.run.app/r/ABC1D23-2F8A91B0",
  "highlights": [],
  "checks": [],
  "reportSections": []
}
POST/v1/vehicle-transfersBearer Firebase ID token

Solicitar transferência

Envia o veículo para o e-mail de outro usuário. O novo dono precisa aceitar no app.

URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/vehicle-transfers
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "vehicleID": "vehicle-id",
  "recipientEmail": "comprador@email.com"
}
Response
{
  "id": "transfer-id",
  "vehicleID": "vehicle-id",
  "vehiclePlate": "ABC1D23",
  "vehicleTitle": "Toyota Corolla",
  "fromOwnerID": "uid-vendedor",
  "fromOwnerEmail": "vendedor@email.com",
  "toOwnerID": "uid-comprador",
  "toOwnerEmail": "comprador@email.com",
  "status": "pending"
}
POST/v1/vehicle-transfers/respondBearer Firebase ID token

Aceitar ou recusar transferência

O destinatário aceita ou recusa uma transferência pendente.

  • Use `action: "decline"` para recusar.
  • Ao aceitar, histórico, documentos e dossiê acompanham o veículo.
URL e método
POST https://cardocs-backend-5qq5b33fha-rj.a.run.app/v1/vehicle-transfers/respond
Headers enviados
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
Accept: application/json
Body enviado
{
  "transferID": "transfer-id",
  "action": "accept"
}
Response
{
  "transfer": {
    "id": "transfer-id",
    "status": "accepted"
  },
  "dashboard": {
    "garages": [],
    "incomingVehicleTransfers": [],
    "outgoingVehicleTransfers": []
  }
}

Erros e retries

Tratamento esperado no Android

  • 400: payload inválido. Mostre a mensagem amigável e corrija o formulário.
  • 401: token ausente, expirado ou inválido. Force refresh uma vez.
  • 404: recurso não encontrado ou provider sem resultado.
  • 500: falha inesperada. Mostre estado de erro e permita tentar novamente.
Validation error
{
  "error": "validation_error",
  "message": "Requisicao invalida.",
  "details": [
    {
      "path": "vehicleID",
      "message": "Too small: expected string to have >=1 characters"
    }
  ]
}
Upload base64
fun File.toBase64Payload(mimeType: String): InvoiceDocumentContent {
    val bytes = readBytes()
    require(bytes.size <= 20_000_000) { "Documento maior que o limite da API" }

    return InvoiceDocumentContent(
        mimeType = mimeType,
        base64Data = Base64.encodeToString(bytes, Base64.NO_WRAP)
    )
}

Checklist de produção

Antes de liberar o app Android

Firebase Auth configurado no projeto Android correto.
Refresh de ID token implementado para 401.
Timeouts e loading states por tela, não globais.
Base64 limitado a 20 MB antes do envio.
Dashboard recarregado após mutações.
Nenhuma escrita direta do app no Firestore.
Logs sem token, base64, CPF, e-mail sensível ou credenciais.
Feature flags locais para desligar rotas em incidente.