{
  "openapi": "3.1.0",
  "info": {
    "title": "Zign API v1",
    "version": "1.2.0",
    "description": "API REST de Zign para firma electrónica y constancias de conservación NOM-151 emitidas por un PSC acreditado (CODEX). Autenticación por llave de organización.",
    "contact": {
      "name": "Soporte Zign",
      "email": "soporte@zerozign.com",
      "url": "https://zerozign.com/api"
    }
  },
  "servers": [
    {
      "url": "https://zerozign.com/api/public/v1",
      "description": "Producción"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "NOM-151",
      "description": "Constancias de conservación de mensajes de datos"
    },
    {
      "name": "Sobres",
      "description": "Firma electrónica de documentos"
    },
    {
      "name": "Verificación",
      "description": "Verificación pública sin llave"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "cfk_<prefijo>_<secreto>"
      }
    },
    "schemas": {
      "Constancia": {
        "type": "object",
        "properties": {
          "sha256": {
            "type": "string",
            "example": "9ad0f1c2…"
          },
          "folio": {
            "type": [
              "string",
              "null"
            ],
            "description": "Folio (UUID) de la constancia del PSC"
          },
          "provider": {
            "type": "string",
            "example": "codex"
          },
          "hashProcessed": {
            "type": [
              "string",
              "null"
            ]
          },
          "issuedAt": {
            "type": "string",
            "format": "date-time"
          },
          "environment": {
            "type": "string",
            "enum": [
              "pruebas",
              "produccion"
            ]
          },
          "fileName": {
            "type": [
              "string",
              "null"
            ]
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "constanciaBase64": {
            "type": [
              "string",
              "null"
            ],
            "description": "Constancia ASN.1/CMS en base64; guárdala como archivo .asn1"
          },
          "status": {
            "type": "string",
            "example": "certificado"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "boolean",
            "example": true
          },
          "code": {
            "type": "string",
            "example": "insufficient_credits"
          },
          "message": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Llave inválida o ausente",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "La llave no tiene el alcance requerido",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "NOM-151"
        ],
        "summary": "Comprobar la llave y la disponibilidad del servicio",
        "responses": {
          "200": {
            "description": "Servicio disponible"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/nom151/constancias": {
      "post": {
        "tags": [
          "NOM-151"
        ],
        "summary": "Emitir una constancia NOM-151",
        "description": "Envía la huella SHA-256 del archivo (recomendado) o el archivo en base64 para que Zign la calcule. La operación es idempotente por organización y huella: repetirla devuelve la constancia existente sin consumir otro certificado. Consume 1 certificado.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sha256": {
                    "type": "string",
                    "description": "Huella del archivo, 64 hex"
                  },
                  "contentBase64": {
                    "type": "string",
                    "description": "Archivo completo en base64 (máx. 20 MB)"
                  },
                  "fileName": {
                    "type": "string"
                  },
                  "externalId": {
                    "type": "string",
                    "description": "Tu identificador en el sistema de origen"
                  }
                }
              },
              "examples": {
                "porHuella": {
                  "value": {
                    "sha256": "3f786850e387550fdab836ed7e6dc881de23001b3f0cbc4b0d1a4f4a1d9ca1a5",
                    "fileName": "contrato-4471.pdf",
                    "externalId": "ERP-4471"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Constancia ya existente (idempotente)"
          },
          "201": {
            "description": "Constancia emitida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Constancia"
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida"
          },
          "402": {
            "description": "Sin certificados disponibles"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "description": "Proveedor de certificación no disponible"
          }
        }
      },
      "get": {
        "tags": [
          "NOM-151"
        ],
        "summary": "Listar constancias de la organización",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Listado paginado"
          }
        }
      }
    },
    "/nom151/constancias/{folio}": {
      "get": {
        "tags": [
          "NOM-151"
        ],
        "summary": "Recuperar una constancia por folio o por huella",
        "parameters": [
          {
            "name": "folio",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Constancia",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Constancia"
                }
              }
            }
          },
          "404": {
            "description": "No encontrada"
          }
        }
      }
    },
    "/nom151/validate": {
      "post": {
        "tags": [
          "NOM-151"
        ],
        "summary": "Validar una constancia contra el PSC",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sha256",
                  "folio",
                  "constanciaBase64"
                ],
                "properties": {
                  "sha256": {
                    "type": "string"
                  },
                  "folio": {
                    "type": "string"
                  },
                  "constanciaBase64": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado de la validación"
          }
        }
      }
    },
    "/verify/{sha256}": {
      "get": {
        "tags": [
          "Verificación"
        ],
        "summary": "Verificación pública de una huella (sin llave)",
        "security": [],
        "parameters": [
          {
            "name": "sha256",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Integridad, NOM-151, blockchain y sello"
          },
          "404": {
            "description": "Huella no registrada"
          }
        }
      }
    },
    "/envelopes": {
      "get": {
        "tags": [
          "Sobres"
        ],
        "summary": "Listar sobres",
        "responses": {
          "200": {
            "description": "Listado"
          }
        }
      },
      "post": {
        "tags": [
          "Sobres"
        ],
        "summary": "Crear (y enviar) un sobre de firma",
        "responses": {
          "201": {
            "description": "Sobre creado"
          }
        }
      }
    },
    "/envelopes/{id}": {
      "get": {
        "tags": [
          "Sobres"
        ],
        "summary": "Estatus del sobre",
        "responses": {
          "200": {
            "description": "Detalle"
          }
        }
      }
    },
    "/envelopes/{id}/send": {
      "post": {
        "tags": [
          "Sobres"
        ],
        "summary": "Enviar un sobre en borrador",
        "responses": {
          "200": {
            "description": "Enviado"
          }
        }
      }
    },
    "/envelopes/{id}/documents/{documentId}": {
      "get": {
        "tags": [
          "Sobres"
        ],
        "summary": "Enlace al PDF certificado y constancia NOM-151",
        "responses": {
          "200": {
            "description": "Enlaces y constancia"
          }
        }
      }
    }
  }
}