Convenciones
Convenciones
Las reglas que rigen en todos los endpoints, enunciadas una vez aquí en lugar de repetirse veinticinco veces.
Idempotencia
Los endpoints que modifican algo aceptan una cabecera Idempotency-Key. Repetir una solicitud con la misma clave reproduce la respuesta original en lugar de repetir el efecto, de modo que un reintento tras un tiempo de espera agotado no puede crear un segundo pasaporte. Las claves pertenecen a una organización, así que dos clientes nunca chocan con el mismo valor. La prueba y la producción están separadas: una clave ya usada en un modo se rechaza en el otro en lugar de reproducirse. Una excepción, y es una capacidad más que un hecho: la URL de subida firmada que devuelve `POST /documents` se vuelve a emitir en la repetición mientras el archivo siga pendiente, y se omite una vez que ha llegado; una URL caducada sería la repetición fiel de algo inservible.
Una clave se recuerda durante 30 días. Pasado ese plazo se olvida por completo, y un reintento que la lleve vuelve a ejecutarse en lugar de reproducirse, así que un proceso de conciliación que pueda ejecutarse más tarde debería generar una clave nueva.
curl "https://passportcraft.com/api/v1/organizations/{organization}/passports" \
-X POST \
-H "Authorization: Bearer $PASSPORTCRAFT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"category":"textile"}'Paginación
Las colecciones se paginan por cursor, nunca por desplazamiento. Un desplazamiento es incorrecto con escrituras concurrentes: un registro insertado entre dos páginas desplaza a todos los siguientes, y un cliente se salta o duplica filas sin enterarse.
Devuelva next_cursor como cursor para obtener la página siguiente. Un cursor codifica los filtros con los que se emitió, así que enviarlo con otros filtros se rechaza en vez de responder con una página que en silencio significaría otra cosa.
{
"object": "list",
"data": [],
"has_more": true,
"next_cursor": "eyJjIjoiMjAyNi0wOC0wMVQxNDowMzoyMi41MDFaIn0"
}Escrituras condicionales
La lectura de un pasaporte devuelve su versión, en el cuerpo de la respuesta y como ETag. Tome la versión del cuerpo de la respuesta en lugar de analizar el ETag: nuestra CDN puede debilitar el ETag cuando comprime, así que al quitar las comillas puede quedar un prefijo W/. En un PATCH, devuelva esa versión en Expected-Version: la escritura se rechaza si el pasaporte cambió entretanto, que es la única forma de hacer segura una secuencia de leer, modificar y escribir frente a un editor concurrente. No use If-Match. Los dos puntos finales que lo leen —actualizar un pasaporte y desvincular un documento— rechazan cualquier valor que no sea el comodín *, porque nuestra CDN compara la cabecera con la respuesta y reescribe el resultado, de modo que la escritura se aplicaría y aun así se le comunicaría como un rechazo. En el resto lo ignoramos, pero puede que nuestra CDN no: la hemos visto sustituir un éxito por un 412 después de nuestra respuesta, así que lo más seguro es no enviar la cabecera. Un PATCH correcto no devuelve ningún ETag; la nueva versión está en el cuerpo de la respuesta, en el campo version. Reintente una escritura rechazada con una Idempotency-Key NUEVA: el rechazo queda registrado con la anterior y se reproduciría.
curl "https://passportcraft.com/api/v1/organizations/{organization}/passports/{id}" \
-X PATCH \
-H "Authorization: Bearer $PASSPORTCRAFT_API_KEY" \
-H "Expected-Version: 4" \
-H "Content-Type: application/json" \
-d '{"data":{"recycled_content_percentage":38}}'Límites de tasa
En cada solicitud autenticada funcionan tres contadores a la vez y decide el más estrecho. Las cifras publicadas son puntos de partida deliberadamente prudentes, no techos ajustados.
| Contador | Solicitudes | Ventana |
|---|---|---|
| key_org | 1000 | 60s |
| organization | 3000 | 60s |
| credential | 10000 | 60s |
| destructive | 50 | 3600s |
Cada respuesta autenticada contabilizada incluye las cabeceras RateLimit y RateLimit-Policy, que indican el contador más cercano a su límite. Un rechazo añade Retry-After, en todas las rutas. Espere ese tiempo en lugar de suponerlo.
Una solicitud que llega sin una clave utilizable se mide con un contador aparte, mucho más estrecho: 60 solicitudes por minuto y dirección de cliente. Nunca se aplica a una clave válida.
Tamaño de la solicitud
Un cuerpo que supera el tope de su endpoint se rechaza con payload_too_large antes de leer nada hacia la base de datos. El tope se comprueba dos veces: primero contra el Content-Length declarado, que no cuesta nada, y después contra los bytes que llegan de verdad, de modo que una solicitud que no declara longitud queda acotada igual.
| Endpoints | Tope |
|---|---|
| Todo endpoint que escribe un único registro | 256 KiB (262144 bytes) |
| Importación masiva y lotes de unidades | 4 MiB (4194304 bytes) |
El tope de los lotes sale del lote más grande que acepta esta API, no de una cifra redonda: 500 filas de importación con todos los campos de una categoría rellenos, o 1000 unidades serializadas. Ninguno cabe en el tope estándar, y por eso esos dos endpoints tienen uno propio.
El cuerpo debe llegar como application/json; application/merge-patch+json también se acepta. Cualquier otro tipo se rechaza con unsupported_media_type en vez de fallar dentro del analizador; el caso que conviene conocer es curl -d, que envía application/x-www-form-urlencoded salvo que se indique otra cosa. Un cuerpo enviado sin ningún Content-Type se lee como JSON.
Métodos HTTP
Un endpoint responde solo a los métodos que documenta. Cualquier otro se rechaza con method_not_allowed y una cabecera Allow que nombra los que sí funcionan, así que un cliente que se equivoca de verbo aprende el correcto en el propio rechazo. HEAD funciona allí donde funciona GET, y OPTIONS funciona en todas partes.
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, PATCH, OPTIONS
Content-Type: application/json
{
"error": {
"code": "method_not_allowed",
"documentation_url": "https://passportcraft.com/docs/api/errors#method_not_allowed",
"request_id": "req_9Fv2KpQ0aXbT4Lmn",
"details": { "method": "DELETE", "allowed": ["GET", "HEAD", "PATCH", "OPTIONS"] }
}
}Operaciones destructivas
Despublicar un pasaporte y enviarlo a la papelera retiran del mercado un registro publicado, así que ambas consumen un tope aparte, además del límite de tasa ordinario. Un bucle de solicitudes baratas y bien formadas no debe poder apagar un catálogo entero.
Adjuntar un documento a un campo
Algunos campos de tipo url admiten un archivo subido en lugar de un enlace: un informe de ensayo o una declaración de conformidad, por ejemplo. Suba primero el archivo y después apunte el campo hacia él escribiendo el identificador del documento en la clave asociada de ese campo, dentro de `data`. El campo puede quedar vacío: un campo que admite documentos se considera cumplimentado en cuanto se le adjunta uno, de modo que un pasaporte con la url vacía sigue pudiendo validarse y publicarse.
Nunca tiene que construir esa clave a mano. Un campo que admite documentos lleva dos atributos en el esquema de la categoría: `document_types`, que enumera los tipos aceptados, y `document_id_key`, que indica la clave exacta que debe escribirse. Lea la clave del esquema en lugar de dar por supuesta la regla de nomenclatura: es la única vía admitida. La clave asociada es una propiedad del campo, no un campo en sí, por lo que nunca aparece en la lista `fields` del esquema.
Una regla que el esquema no detalla: el `access_tier` del documento debe coincidir con el del campo. Un documento público no puede satisfacer un campo de nivel «authority»; la vinculación se rechaza con `access_tier_mismatch`, porque un documento con un nivel distinto al del campo no se mostraría allí donde el campo sí se muestra. Cree el documento con el `access_tier` que indique el descriptor del campo.
curl "https://passportcraft.com/api/v1/organizations/{organization}/passports/{id}" \
-X PATCH \
-H "Authorization: Bearer $PASSPORTCRAFT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"data":{"conformity_declaration_url__document_id":"doc_123"}}'Vacíe la clave asociada para desvincular el documento; el archivo permanece en su biblioteca hasta que lo elimine. Al eliminar un documento al que todavía apunta un campo, la respuesta indica qué se desvinculó, y ese aviso solo llega a quien realizó la petición.
El `upload_state` de un documento es `ready`, `pending` o `unknown`. `unknown` significa que no hemos podido establecer si los bytes están en el almacenamiento: o bien el almacenamiento no respondió, o bien la fila quedó más allá de los 25 documentos sin bytes que comprueba cada petición. Considérelo indeterminado, no ausente: al publicar, `unknown` produce `storage_unavailable` en lugar de afirmar que falta un archivo, y una lectura posterior suele resolverlo.
Campos que solo se aplican a algunos productos
El indicador `required` de un campo significa que el control de publicación puede exigirlo. Cuando esa exigencia depende del propio producto, el campo lleva además `required_when`, que nombra otro campo y los valores que activan la exigencia. El ensayo de referencia de la vida en ciclos lleva `required_when` para `battery_category` con `ev` y `lmt`: el Reglamento sobre baterías incluye los datos del pasaporte «en la medida en que sea aplicable a la categoría o subcategoría de batería de que se trate», y las orientaciones de la Comisión sobre el pasaporte de baterías asignan cada punto de datos a las categorías a las que se aplica. Una categoría también puede volver a exigir un campo mediante una regla que lee varios campos a la vez: `required_when` no las describe, y es el punto final de validación el que las resuelve.
Lea ambos juntos y trate `required` como la respuesta más amplia: un cliente que ignore `required_when` como mucho pedirá más de lo necesario. Nunca dará un pasaporte por listo para que después se le rechace la publicación. Para un pasaporte concreto, la respuesta exacta la da el punto final de validación, que tiene ese pasaporte delante.
Campos obligatorios a partir de una fecha
Un campo con `required_from` pasa a ser obligatorio para publicar en esa fecha, según la hora de Bruselas; antes de esa fecha, es opcional. Publicamos esa fecha lo antes posible, normalmente con al menos 60 días de antelación; si un reglamento fija o modifica una fecha con menos antelación, el campo se rige por la fecha del reglamento. Lea `required` junto con `required_when` y `required_from`; el punto final de validación da la respuesta para un pasaporte concreto.
Lecturas de batería
Cada lectura registra un valor, su fuente y la fecha en que se midió o declaró. Para corregir un valor, registre otra lectura. Prevalece la fecha de observación más reciente; si coincide, prevalece la entrada posterior.
per_battery: override
Una batería utiliza el valor de su modelo hasta que tenga una lectura propia para ese campo. Un valor del modelo no tiene fecha de medición.
Valores iniciales para cada batería (opcional)
model_health_declaration- Mientras se aplique la declaración, cada valor de estado de salud procede del modelo hasta que registre ese valor para la batería.
- Los valores que falten en el modelo permanecen en blanco. Con esta declaración, el estado de energía certificada comienza en el 100 %.
- Registrar un valor sustituye la declaración del modelo solo para ese campo.
- El primer estado que guarde mantiene la declaración. Si vuelve a guardar un estado, la declaración deja de proporcionar valores de estado de salud para esa batería. Esto se aplica aunque elija el mismo estado.
- Los valores ya registrados para esa batería se conservan.
Los valores obligatorios de batería que falten se indican en missing_fields. No impiden crear una batería ni publicar su modelo.
Identificadores de solicitud
Cada respuesta autenticada incluye una cabecera Request-Id, tanto en caso de éxito como de error. Cítela en una solicitud de soporte y podremos localizar la llamada exacta. Los dos endpoints de categorías responden a las peticiones correctas sin clave desde una caché compartida, por lo que esas respuestas no incluyen Request-Id; un rechazo siempre la incluye, en todas las rutas.
El flujo de eventos conserva 30 días de historial. Una integración que consulte con menos frecuencia perderá cambios y debería conciliar contra la lista de pasaportes.