DOCS

Integrar Inspección App

Embebe el enlace de inspección en un iframe o en un WebView nativo y recibe una notificación en tu aplicación cuando el usuario finaliza la inspección.

Inspection App es la aplicación web que el usuario abre desde el magicLink para realizar la inspección. Además de distribuir el enlace por WhatsApp, SMS o email, puedes mostrar Inspection App dentro de tu propio producto embebiendo el magicLink en un iframe (web) o en un WebView (iOS, Android, React Native o Flutter). Autoinspector no bloquea el embebido de la aplicación.

Cuando el usuario finaliza la inspección, Inspection App emite un evento hacia tu aplicación para que puedas reaccionar en la interfaz, por ejemplo cerrar el WebView, desmontar el iframe o iniciar una animación de transición.

Evento de inspección finalizada

Inspection App emite un único evento hacia tu aplicación: inspection_client_completed. El mensaje tiene la siguiente estructura:

{
  "event": "inspection_client_completed",
  "payload": {
    "_id": "65f8f73aa4a5b1c2d3e4f5f0",
    "status": "processing",
    "testing": false,
    "metadata": {
      "policy_id": "POL-10021",
      "external_claim_id": "CLM-92811"
    },
    "createdAt": "2025-03-01T10:00:00.000Z",
    "startedAt": "2025-03-01T10:05:00.000Z",
    "finishedAt": "2025-03-01T10:20:00.000Z"
  }
}
CampoDescripción
payload._idIdentificador de la inspección.
payload.statusEstado de la inspección luego de la finalización. Normalmente processing.
payload.testingIndica si la inspección fue creada como inspección de prueba.
payload.metadataMetadata definida por tu sistema al crear la inspección. Úsala para correlacionar con tus registros.
payload.createdAtFecha de creación de la inspección en formato ISO 8601.
payload.startedAtFecha en la que el usuario comenzó la inspección. Opcional.
payload.finishedAtFecha en la que el usuario finalizó la inspección. Opcional.

El mensaje siempre se envía serializado como un string JSON, en todas las plataformas, para que el formato sea idéntico sin importar el mecanismo de comunicación del WebView. Debes parsearlo con JSON.parse o el equivalente de tu plataforma.

El evento no incluye recursos, inferencias, validaciones ni el resultado de la inspección, ya que en ese instante todavía no fueron procesados. Si tu sistema necesita esa información, utiliza los webhooks.

Momento de emisión

El evento se emite inmediatamente después de que Autoinspector confirma la finalización de la inspección y antes de que Inspection App muestre su pantalla final. Si la finalización falla, por ejemplo por un problema de red, el evento no se emite y el usuario puede reintentar desde la aplicación.

Inspection App no emite ningún otro evento cuando el usuario abandona la pantalla final. La decisión de cuándo desmontar el iframe o cerrar el WebView es de tu aplicación: puedes hacerlo apenas recibes inspection_client_completed o esperar el tiempo que consideres necesario para que el usuario vea la pantalla final.

Continuación desde el código QR en escritorio

Cuando una inspección embebida requiere un dispositivo móvil, el iframe de escritorio muestra un código QR. Mantén ese iframe montado mientras el usuario completa la inspección desde su teléfono. El escritorio consulta la confirmación del envío cada pocos segundos mientras su pestaña está visible y luego emite el mismo evento inspection_client_completed hacia la página que lo embebe. Las consultas se pausan mientras la pestaña está oculta y se reanudan cuando vuelve a estar visible. La expiración o el bloqueo no emiten un evento de finalización.

Procesa este evento una sola vez por inspección en tu aplicación, incluso si se recarga el iframe. Para coordinar el backend sin depender del navegador, suscríbete al webhook inspection_client_completed. Los resultados procesados llegan por separado mediante inspection_completed.

Escuchar el evento según la plataforma

En todas las plataformas nativas el nombre del handler o canal que debes registrar es autoinspector. En la web el evento llega mediante window.postMessage al documento que embebe el iframe.

Embebe el magicLink en un iframe. Como la inspección utiliza la cámara del dispositivo, habilita los permisos correspondientes mediante el atributo allow.

<iframe
  id="inspection-app"
  src="https://app.autoinspector.ai/inspection/65f8f73aa4a5b1c2d3e4f5f0?accessCode=1074"
  allow="camera; microphone; geolocation"
  style="border: 0; width: 100%; height: 100vh"
></iframe>

Escucha el evento message en tu página y valida el origen antes de procesarlo:

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://app.autoinspector.ai') return

  const message = JSON.parse(event.data)

  if (message.event !== 'inspection_client_completed') return

  console.log('inspection finished by client', message.payload._id)

  document.getElementById('inspection-app').remove()
})

Inspection App envía el mensaje únicamente al origen de la página que embebe el iframe, utilizando el referrer del documento. Asegúrate de que tu página no defina la política Referrer-Policy: no-referrer, ya que en ese caso Inspection App no puede conocer tu origen y omite el envío.

Registra un WKScriptMessageHandler con el nombre autoinspector en la configuración del WKWebView. El mensaje llega en message.body como un String con el JSON del evento.

import WebKit

struct InspectionAppEvent: Decodable {
  let event: String
}

final class InspectionViewController: UIViewController, WKScriptMessageHandler {
  private var webView: WKWebView!

  override func viewDidLoad() {
    super.viewDidLoad()

    let configuration = WKWebViewConfiguration()
    configuration.userContentController.add(self, name: "autoinspector")
    configuration.allowsInlineMediaPlayback = true

    webView = WKWebView(frame: view.bounds, configuration: configuration)
    view.addSubview(webView)

    let magicLink = URL(string: "https://app.autoinspector.ai/inspection/65f8f73aa4a5b1c2d3e4f5f0?accessCode=1074")!
    webView.load(URLRequest(url: magicLink))
  }

  func userContentController(
    _ userContentController: WKUserContentController,
    didReceive message: WKScriptMessage
  ) {
    guard
      message.name == "autoinspector",
      let body = message.body as? String,
      let data = body.data(using: .utf8),
      let event = try? JSONDecoder().decode(InspectionAppEvent.self, from: data)
    else { return }

    if event.event == "inspection_client_completed" {
      dismiss(animated: true)
    }
  }
}

Recuerda implementar WKUIDelegate.webView(_:requestMediaCapturePermissionFor:initiatedByFrame:type:decisionHandler:) para otorgar acceso a la cámara dentro del WebView.

Registra un listener con el nombre autoinspector mediante WebViewCompat.addWebMessageListener (AndroidX webkit). El mensaje llega en message.data como un String con el JSON del evento.

import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewFeature
import org.json.JSONObject

val allowedOrigins = setOf("https://app.autoinspector.ai")

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
  WebViewCompat.addWebMessageListener(webView, "autoinspector", allowedOrigins) { _, message, _, _, _ ->
    val raw = message.data ?: return@addWebMessageListener
    val event = JSONObject(raw)

    if (event.getString("event") == "inspection_client_completed") {
      finish()
    }
  }
}

webView.settings.javaScriptEnabled = true
webView.loadUrl("https://app.autoinspector.ai/inspection/65f8f73aa4a5b1c2d3e4f5f0?accessCode=1074")

Si no puedes usar AndroidX webkit, la alternativa es addJavascriptInterface con el mismo nombre y un método postMessage(String):

class InspectionAppBridge(private val onEvent: (String) -> Unit) {
  @JavascriptInterface
  fun postMessage(message: String) = onEvent(message)
}

webView.addJavascriptInterface(
  InspectionAppBridge { raw ->
    val event = JSONObject(raw)

    if (event.getString("event") == "inspection_client_completed") {
      runOnUiThread { finish() }
    }
  },
  "autoinspector",
)

Recuerda implementar WebChromeClient.onPermissionRequest para otorgar acceso a la cámara dentro del WebView.

Con react-native-webview el evento llega en la prop onMessage. No es necesario registrar ningún handler adicional.

import { WebView } from 'react-native-webview'

export const InspectionScreen = ({ magicLink, onCompleted }) => (
  <WebView
    source={{ uri: magicLink }}
    mediaPlaybackRequiresUserAction={false}
    allowsInlineMediaPlayback
    onMessage={(event) => {
      const message = JSON.parse(event.nativeEvent.data)

      if (message.event !== 'inspection_client_completed') return

      onCompleted(message.payload)
    }}
  />
)

Con flutter_inappwebview registra un JavaScript handler con el nombre autoinspector. El primer argumento del callback es un String con el JSON del evento.

import 'dart:convert';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

InAppWebView(
  initialUrlRequest: URLRequest(url: WebUri(magicLink)),
  onWebViewCreated: (controller) {
    controller.addJavaScriptHandler(
      handlerName: 'autoinspector',
      callback: (args) {
        final message = jsonDecode(args.first as String) as Map<String, dynamic>;

        if (message['event'] == 'inspection_client_completed') {
          Navigator.of(context).pop(message['payload']);
        }
      },
    );
  },
  onPermissionRequest: (controller, request) async {
    return PermissionResponse(
      resources: request.resources,
      action: PermissionResponseAction.GRANT,
    );
  },
)

Si utilizas webview_flutter, registra un JavaScriptChannel con el nombre autoinspector. El canal expone window.autoinspector.postMessage, que Inspection App también utiliza para emitir el evento.

controller.addJavaScriptChannel(
  'autoinspector',
  onMessageReceived: (message) {
    final event = jsonDecode(message.message) as Map<String, dynamic>;

    if (event['event'] == 'inspection_client_completed') {
      Navigator.of(context).pop(event['payload']);
    }
  },
);

En esta página