API REST — Integración Zoho Sign
1. Introducción y objetivo
Esta documentación describe la API REST desarrollada por Control Zeta Digital para integrar Zoho Sign con sitios WordPress/WooCommerce que utilizan el plugin WPMoodle.
El objetivo principal de esta API es consultar y actualizar el estado de firma asociado a un pedido de WooCommerce, de forma que el sistema retrase la matriculación en Moodle hasta que el proceso de firma electrónica haya finalizado correctamente.
La API actúa como punto de comunicación entre Zoho Sign (o sistemas intermedios) y los sitios web del cliente, garantizando que la matrícula solo se ejecute cuando el estado de firma sea signed.
2. Entornos y URLs base
Desarrollo
https://dinam.thecortex.pro/wp-json/zoho-sign/v1
Producción
https://ineforma.com/wp-json/zoho-sign/v1
https://psicomagister.com/wp-json/zoho-sign/v1
https://inesbe.com/wp-json/zoho-sign/v1
https://edocentes.com/wp-json/zoho-sign/v1
https://hoturis.com/wp-json/zoho-sign/v1
Nota: los endpoints y el comportamiento son idénticos en todos los entornos.
3. Autenticación
La API utiliza Basic Authentication mediante las credenciales de la WooCommerce REST API.
Requisitos:
- Consumer Key (
ck_xxx) - Consumer Secret (
cs_xxx) - Permisos: read / write
Header de autenticación
El header se construye codificando en Base64 la cadena:
Authorization: Basic base64(ck_xxx:cs_xxx)
Ejemplo en cURL:
curl -u ck_xxx:cs_xxx https://example.com/wp-json/zoho-sign/v1/orders/123
4. Endpoints (MVP)
4.1 GET /orders/{order_id}
Devuelve el estado actual de firma asociado a un pedido.
Request
GET /orders/{order_id}
Response 200:
{
"success": true,
"data": {
"order_id": 123,
"signature_status": "pending",
"signature_updated_at": "2026-02-06T12:34:56+00:00",
"external_ref": "ZS-ABC-123"
}
}
Campos:
order_id: ID del pedido WooCommercesignature_status: estado actual onullsignature_updated_at: fecha ISO8601 onullexternal_ref: referencia externa (Zoho Sign u otro sistema)
4.2 POST /orders/{order_id}
Actualiza el estado de firma de un pedido ya inicializado.
Request
POST /orders/{order_id}
Content-Type: application/json
Body:
{
"signature_status": "signed",
"external_ref": "ZS-ABC-123",
"note": "Documento firmado correctamente"
}
Response 200
{
"success": true,
"data": {
"order_id": 123,
"signature_status": "signed",
"signature_updated_at": "2026-02-06T13:10:22+00:00",
"external_ref": "ZS-ABC-123"
}
}
5. Estados de firma
Estados soportados:
pending(estado interno)signed(final)rejected(final)expired(final)failed(final)
Consideraciones clave
pendingsolo puede ser asignado por el sistema- La API externa NO puede enviar
pending - Los estados finales no admiten transiciones posteriores
6. Flujo de vida del pedido y la firma
Flujo general
- El pedido se crea en WooCommerce
- El pedido pasa a estado completed
- El sistema:
- asigna
signature_status = pending - espera a que se produzcan cambios de estado
- asigna
- Zoho Sign procesa la firma
- El sistema externo llama a la API:
POST /orders/{order_id}- con el estado final (
signed,rejected, etc.)
- Si el estado es
signed:- se dispara un hook interno
- continúa el proceso de matriculación en Moodle
7. Reglas y restricciones importantes
Lo que SÍ debe cumplirse
- El pedido debe estar en estado completed
- El estado actual debe ser pending
- El nuevo estado debe ser final
Lo que NO está permitido
- Inicializar el estado vía API (cuando es null)
- Enviar pending desde sistemas externos
- Cambiar un estado final por otro
- Actualizar pedidos no completados
8. Errores y respuestas esperables
401 — Credenciales inválidas
{
"code": "rest_forbidden",
"message": "Invalid credentials"
}
404 — Pedido no encontrado
{
"code": "order_not_found",
"message": "Order not found"
}
409 — Pedido no completado
{
"code": "order_not_completed",
"message": "Order is not completed"
}
409 — Estado de firma no inicializado
{
"code": "signature_not_initialized",
"message": "Signature status is null"
}
403 — Intento de enviar pending
{
"code": "pending_not_allowed",
"message": "Pending status cannot be set via API"
}
409 — Transición inválida
{
"code": "signature_not_editable",
"message": "Signature status cannot be changed"
}
422 — Estado inválido
{
"code": "validation_error",
"message": "Invalid signature status value"
}
9. Caché (nota técnica importante)
Las webs de Dinam Soluciones utilizan Cloudflare como capa edge para CDN y seguridad. Para que la API funcione correctamente, los endpoints bajo /wp-json/zoho-sign/v1/* deben excluirse de las reglas de caché de Cloudflare y del servidor, garantizando así que se devuelvan siempre datos actualizados.
- La API envía headers
no-cache - Se recomienda:
- bypass explícito en Cloudflare por ruta
- desactivar caché a nivel de servidor
10. Troubleshooting
El GET devuelve datos antiguos
- Revisar los headers de la respuesta HTTP y confirmar que no se está cacheando:
curl -s -D - -o /dev/null \
-H "Authorization: Basic BASE64_AUTH" \
"https://example.com/wp-json/zoho-sign/v1/orders/{order_id}"
Comprobar especialmente
cf-cache-status- Debe ser
DYNAMICoBYPASS - Si es
HIT, la respuesta está cacheada por Cloudflare
- Debe ser
-
Headers de servidor/cache:
x-srcache-fetch-statusx-cachecache-control
-
Asegurar bypass de caché por ruta en Cloudflare:
/wp-json/zoho-sign/v1/*
- Verificar que la respuesta incluya headers
no-cache, por ejemplo:
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Pragma: no-cache
El POST devuelve 409 Conflict
Verificar que se cumplan todas las condiciones siguientes:
- El pedido está en estado
completed - El estado actual de firma es
pending - El estado enviado NO es
pending - La transición de estado es válida (no se intenta cambiar un estado final)
No se ejecuta la matrícula tras signed
- Confirmar que el estado final enviado es exactamente
signed - Verificar mediante
GETque el pedido refleja:
{
"signature_status": "signed"
}
-
Revisar los logs del sistema y confirmar que:
-
El hook interno asociado a
signedse ha disparado correctamente - El proceso de matriculación no se ha abortado por:
- usuario ya matriculado
- error previo de Moodle
- pedido no válido
El POST responde success pero no hay cambios visibles
- Verificar de nuevo los headers de caché en el
GET - Añadir un parámetro de cache-busting solo para pruebas:
GET /orders/{order_id}?cb=timestamp
Solo para diagnóstico, no para uso normal