AlchemyCode API docs

cURL y shell

Todo AlchemyCode API se puede operar con curl. Esta guía cubre autenticación, alta de empresa, e.firma multipart, consulta con polling, descarga de archivos firmados y diagnóstico de errores.

Preparar el entorno

Shell
export ALCHEMY_API_KEY="ck_live_7f3a91c2m4p8q1w7x5n3v9b6t0"
export API="https://api.434alchemycode.com/v1"
export COMPANY_ID="9b2f5c1e-7a3d-4f8b-9c21-6d4e8a0b5f17"

# Nunca pegues la llave directo en la línea de comandos: queda en el historial.
# Usa una variable de entorno o un archivo con permisos 600 leído por --config.

Endpoints de servicio

RutaServicioModo
/v1/sat/constanciasConstancia SATAsíncrono
/v1/sat/opinionesOpinión 32-DAsíncrono
/v1/sat/domiciliosDomicilio fiscalAsíncrono
/v1/imss/buzonesBuzón IMSSAsíncrono
/v1/imss/opinionesOpinión IMSSAsíncrono
/v1/infonavit/opinionesOpinión InfonavitAsíncrono
/v1/sat/articulo-69bArtículo 69-BSíncrono
/v1/captcha/resolucionesCAPTCHASíncrono

Alta de empresa

Shell
curl -sS -X POST "$API/companies" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rfc": "OMX2503255F9",
    "legal_name": "OMNICONMX, S.A. de C.V.",
    "tax_regime": "601",
    "external_ref": "OMNICOM-001"
  }' | python3 -m json.tool

Carga de e.firma

Shell
curl -sS -X POST "$API/companies/$COMPANY_ID/credentials/sat" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  -F "cer=@./omnicom.cer;type=application/x-x509-ca-cert" \
  -F "key=@./omnicom.key;type=application/octet-stream" \
  -F "password=$FIEL_PASSWORD" | python3 -m json.tool
No agregues -H "Content-Type: multipart/form-data": cURL debe generar el boundary. Si lo fijas a mano, el servidor no puede separar las partes.

Consulta con polling

Este script no depende de jq: usa python3 para leer el JSON. Funciona en macOS y Linux.

Shellconsulta.sh
#!/usr/bin/env bash
set -euo pipefail

: "${ALCHEMY_API_KEY:?Falta ALCHEMY_API_KEY}"
API="https://api.434alchemycode.com/v1"
COMPANY_ID="9b2f5c1e-7a3d-4f8b-9c21-6d4e8a0b5f17"

job=$(curl -sS -X POST "$API/sat/constancias" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen | tr '[:upper:]' '[:lower:]')" \
  -d "{\"company_id\":\"$COMPANY_ID\",\"include_pdf\":true}")

job_id=$(printf '%s' "$job" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
echo "Job: $job_id"

for attempt in $(seq 1 60); do
  payload=$(curl -sS "$API/jobs/$job_id" -H "Authorization: Bearer $ALCHEMY_API_KEY")
  state=$(printf '%s' "$payload" | python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])')
  echo "Intento $attempt: $state"

  case "$state" in
    succeeded)
      printf '%s' "$payload" > "job-$job_id.json"
      break
      ;;
    failed|canceled|expired)
      printf '%s\n' "$payload" >&2
      exit 1
      ;;
  esac
  sleep 2
done

# Descarga el primer archivo (URL firmada, expira en 15 min)
file_url=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["files"][0]["url"])' "job-$job_id.json")
curl -sS "https://api.434alchemycode.com$file_url" -o constancia.pdf
echo "PDF: constancia.pdf ($(wc -c < constancia.pdf) bytes)"

Consulta con wait=true

Shell
curl -sS -X POST "$API/sat/opiniones" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"company_id\":\"$COMPANY_ID\",\"wait\":true}" \
  -w '\nHTTP %{{http_code}} en %{{time_total}} s\n'

Si el job no termina en 25 segundos, verás HTTP 202 con el job en curso. Vuelve a consultarlo por /jobs/{id}.

Diagnóstico de errores

Shell
# -i muestra status y cabeceras; el error viene como application/problem+json
curl -i -sS -X POST "$API/sat/constancias" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"company_id": "no-es-un-uuid"}'

# Filtra sólo el código y el request_id (sin jq)
curl -sS "$API/me" -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["plan"]["name"], d["quota"]["used"], "/", d["quota"]["quota"])'
HTTP
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
X-Request-Id: req_2b7e9d1c4a6f8035

{
  "code": "VALIDATION_ERROR",
  "detail": "Revisa los campos marcados en `errors`.",
  "request_id": "req_2b7e9d1c4a6f8035",
  "retryable": false,
  "errors": [{"field": "company_id", "code": "UUID_PARSING", "message": "Debe ser un UUID válido."}]
}

Buenas prácticas con cURL

  • Usa -sS: silencio en progreso, pero errores de red visibles.
  • No pongas la llave en el comando: queda en ~/.bash_history y en ps.
  • Guarda el JSON del job en un archivo antes de descargar archivos.
  • Usa --connect-timeout 10 --max-time 60 en scripts desatendidos.
  • Para lotes de 69-B, envía un archivo con --data-binary @lote.json.
  • Verifica el status con -w '%{http_code}'; no confíes sólo en la ausencia de error de cURL.