Skip to content

Experimentos A/B de campaña ​

English: Campaign A/B experiments.

Un experimento compara de dos a cinco variantes de una misma campaña. Corre en campañas de voz, SMS y correo (ver Campañas de SMS y correo). Créalo como borrador, inícialo cuando las variantes estén listas y deténlo para dejarlo terminal. Las rutas, los cuerpos de petición, los envoltorios de respuesta y los códigos de estado de abajo están copiados de los esquemas Experiment y de las operaciones listCampaignExperiments, createCampaignExperiment, getCampaignExperiment, updateCampaignExperiment, deleteCampaignExperiment, startCampaignExperiment y stopCampaignExperiment del openapi/dialer-v1.yaml vendorizado.

Crear un borrador ​

POST /v1/campaigns/{id}/experiments acepta name, primary_metric y variants. primary_metric es contact_rate o promise_rate. El arreglo de variantes tiene de 2 a 5 entradas, cada una con un name, un weight de 1 a 1,000 y exactamente una entrada con is_control: true.

bash
curl -sS -X POST "$API/v1/campaigns/$CAMPAIGN_ID/experiments" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Window test",
    "primary_metric": "contact_rate",
    "variants": [
      {"name": "Control", "weight": 50, "is_control": true, "overrides": {}},
      {"name": "Afternoon window", "weight": 50, "overrides": {
        "window": {"start": "10:00", "end": "16:00"}
      }}
    ]
  }'

La respuesta es 201 con { "experiment": ... }. Un cuerpo mal formado es 400; un experimento inválido es 422 con error.details, una entrada por cada campo rechazado. window y min_gap_seconds solo aplican a una campaña de voz, y una campaña de SMS o de correo no puede elegir promise_rate (una promesa apunta a una llamada, no a un mensaje): las tres cosas se rechazan con 422 (createCampaignExperiment). Una ventana debe ser un subconjunto de la ventana de la campaña, y un guion de SMS debe cumplir los validadores de la propia campaña, incluido el requisito de STOP.

Qué puede cambiar una variante ​

El objeto overrides es una lista cerrada. Solo se aceptan estas claves:

  • script: { "field": "tts_prompt" | "voicemail_message" | "sms_body", "text": "..." };
  • window: { "start": "HH:MM", "end": "HH:MM" }, nunca más ancha que la ventana de la campaña;
  • min_gap_seconds: un entero de 1 a 2,592,000 (solo campañas de voz, ver más abajo);
  • email_template_id: un UUID de una plantilla aprobada que pertenezca al tenant.

Claves como optout_digit, abandonment_threshold y max_dial_ratio no son controles de un experimento. Se rechazan en lugar de ignorarse. Esta lista cerrada y las restricciones de los campos vienen de ExperimentVariantOverrides en la spec vendorizada; el límite de cumplimiento también está registrado en el ADR #242 de core.

Consultar, editar y borrar ​

  • GET /v1/campaigns/{id}/experiments lista todos los estados, del más nuevo al más antiguo, con las variantes en orden de position.
  • GET /v1/campaigns/{id}/experiments/{experiment_id} devuelve un experimento.
  • PATCH /v1/campaigns/{id}/experiments/{experiment_id} acepta name y/o variants; reemplazar variants reemplaza el conjunto completo. primary_metric solo puede repetirse, no cambiarse.
  • DELETE /v1/campaigns/{id}/experiments/{experiment_id} borra únicamente un borrador y devuelve 204.

Cuando un experimento está en curso o terminado, sus variantes quedan congeladas. Editarlo o borrarlo devuelve 409. Los recursos de otro tenant y los identificadores desconocidos devuelven 404.

Iniciar y detener ​

Inicia un borrador con:

bash
curl -sS -X POST \
  "$API/v1/campaigns/$CAMPAIGN_ID/experiments/$EXPERIMENT_ID/start" \
  -H "Authorization: Bearer $TOKEN"

POST .../start pasa de draft a running y vuelve a validar las variantes contra la campaña actual. Devuelve 422 si las restricciones ya no se cumplen y 409 si ya hay otro experimento en curso o el experimento no es un borrador. Los experimentos corren en campañas de voz, SMS y correo cuyos mensajes o llamadas envía la propia plataforma. Iniciar uno en una campaña de vista previa o manual es 422 (details[].path = "campaign"), aunque la creación del borrador se haya aceptado (startCampaignExperiment).

Detén un experimento en curso con:

bash
curl -sS -X POST \
  "$API/v1/campaigns/$CAMPAIGN_ID/experiments/$EXPERIMENT_ID/stop" \
  -H "Authorization: Bearer $TOKEN"

POST .../stop pasa de running al estado terminal ended; cualquier otro estado actual devuelve 409.

Campañas de SMS y correo ​

En una campaña de SMS o de correo, el experimento lee el libro de mensajes de la campaña en lugar del libro de llamadas (getCampaignExperimentResults):

  • Los intentos son los mensajes salientes originales y sus transiciones de estado. Un mensaje que el proveedor nunca tomó es not_placed, y un contacto es un mensaje delivered.
  • promises y abandoned son siempre 0, así que promise_rate y abandon_rate son null. Usa primary_metric: contact_rate; promise_rate se rechaza al crear.
  • En una campaña de SMS, una variante puede sustituir script con field: "sms_body"; el texto debe seguir cumpliendo los validadores de la propia campaña, incluido STOP. Para el email_template_id de una variante, la plantilla debe ser una aprobada del tenant (ExperimentVariantOverrides).
  • La lista cerrada de overrides, la asignación y las reglas del veredicto son las mismas que en voz.

El release v0.105.0 de core incluye los commits de experimentos de SMS y correo etiquetados core#2057 (los libros de mensajes estampan la variante, los ejecutores la aplican, y los resultados y el inicio aceptan el canal). La spec vendorizada de ese release es la fuente de las líneas de arriba.

La lectura aparte GET /v1/campaigns/{id}/email-skips (campaignEmailSkips) explica por qué una campaña de correo no envió a un lead; es un diagnóstico del ejecutor de correo y no recibe ningún parámetro de experimento, así que no forma parte del contrato de experimentos.

Asignación estable e invariantes de cumplimiento ​

Core asigna una deuda de forma determinista a partir de sha256(experiment_id <> debt_id), usando los primeros 64 bits y los pesos de las variantes en orden de position. Por eso el mismo par experimento/deuda recibe la misma variante entre reintentos; el identificador del experimento forma parte de la entrada, así que experimentos distintos no comparten asignación. Este comportamiento lo define la implementación Dialer.Campaigns.Experiments.Assignment de core y sus pruebas.

El experimento no crea un carril de cumplimiento aparte. Todas las variantes pasan por las mismas puertas de cumplimiento, y la divulgación legal no cambia entre variantes. La lista cerrada de overrides es deliberada: los controles de opt-out, abandono y ratio de marcación no pueden variarse en un experimento. El ADR #242 de core documenta este límite y el ejecutor de campañas conserva la ruta de cumplimiento completa en la originación.

Resultados y veredicto ​

GET /v1/campaigns/{id}/experiments/{experiment_id}/results devuelve { "results": ... }. Se recalcula desde el libro de llamadas en cada lectura (sin contadores), y un draft responde con ceros e insufficient_data. Los errores son 401, 404 y 429.

bash
curl -sS \
  "$API/v1/campaigns/$CAMPAIGN_ID/experiments/$EXPERIMENT_ID/results" \
  -H "Authorization: Bearer $TOKEN"
  • variants[]: por variante assigned_debts, attempts, not_placed, placed, contacts, promises, abandoned, successes, y las tasas contact_rate, promise_rate y abandon_rate (cada una null cuando su denominador es cero). Los intentos que nunca salieron (not_placed) quedan fuera de todos los denominadores.
  • verdict.status es insufficient_data, no_significant_difference o winner. Un winner trae variant_id, versus_variant_id, p_value, difference, ci_low y ci_high; comparisons[] tiene una entrada por retador y sample_floor indica la muestra mínima.
  • allocation_warning es true cuando el reparto observado de las deudas asignadas se desvía de los pesos (chi-cuadrado, alfa 0.001); entonces no se emite ningún winner. verdict.withheld_by es allocation_warning solo cuando se retuvo un ganador que habría existido (el estado es entonces no_significant_difference); en cualquier otro caso es null, incluso con allocation_warning: true.
  • unattributed_attempts es distinto de cero cuando el sello de experimento de un intento y el experimento no coinciden.

Las definiciones de los campos son ExperimentResults, ExperimentVariantResults y ExperimentVerdict en la spec vendorizada (core v0.105.0); las reglas de recálculo y de veredicto están registradas en el ADR #249 de core.

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.