Paginación

Todos los endpoints de lista de la API v3 usan paginación por cursor. Los cursores son opacos: son cadenas que solo AllSign entiende. No los construyas, no los decodifiques y no infieras nada de su contenido — solo pásalos de vuelta tal cual. Un cursor malformado o adivinado devuelve 400 INVALID_CURSOR.

El sobre de lista

Toda lista responde con el mismo sobre (envelope): object: "list", el arreglo data, y los metadatos de paginación. Nunca recibes un arreglo pelón.

{
  "object": "list",
  "data": [ /* ... los recursos de esta página ... */ ],
  "hasMore": true,
  "nextCursor": "djF8Y3JlYXRlZEF0fC4uLg",
  "previousCursor": null,
  "limit": 20,
  "totalCount": null
}
CampoQué es
objectSiempre "list".
dataLos recursos de esta página, en orden.
hasMoretrue si hay más páginas después de esta. Tu señal de fin de loop.
nextCursorCursor opaco para la página siguiente (o null si no hay).
previousCursorCursor opaco para la página anterior (o null).
limitEl tamaño de página efectivo que se aplicó.
totalCountEl total de la colección — null salvo que pidas includeTotal=true.

Parámetros

ParámetroQué hace
limitRecursos por página. Rango 1–100, default 20.
startingAfterCursor: trae la página después de este punto (avanzar).
endingBeforeCursor: trae la página antes de este punto (retroceder).

startingAfter y endingBefore son mutuamente excluyentes: si mandas ambos, obtienes 422 VALIDATION_ERROR. Elige una dirección por petición.

Al paginar, mantén fijos el orden y los filtros entre peticiones. Un cursor está atado al conjunto de sort + filtros con el que lo generaste; cambiarlos a media paginación produce resultados inconsistentes. Y recuerda: si el cursor no es válido para esa consulta, la API responde 400 INVALID_CURSOR.

Recorrer todas las páginas

El patrón correcto es un bucle while guiado por hasMore: mientras sea true, pasa el nextCursor de la respuesta como startingAfter de la siguiente petición. No uses totalCount para decidir cuándo parar.

JavaScript:

async function listAll() {
  const out = [];
  let cursor = null;
  let hasMore = true;

  while (hasMore) {
    const url = new URL("https://api.dev.allsign.io/v3/documents");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("startingAfter", cursor);

    const res = await fetch(url, {
      headers: { Authorization: "Bearer " + process.env.ALLSIGN_KEY },
    });
    const page = await res.json();

    out.push(...page.data);
    hasMore = page.hasMore;
    cursor = page.nextCursor;
  }
  return out;
}

Python:

import os, requests

def list_all():
    out, cursor = [], None
    while True:
        params = {"limit": 100}
        if cursor:
            params["startingAfter"] = cursor
        r = requests.get(
            "https://api.dev.allsign.io/v3/documents",
            headers={"Authorization": f"Bearer {os.environ['ALLSIGN_KEY']}"},
            params=params,
        )
        page = r.json()
        out.extend(page["data"])
        if not page["hasMore"]:
            break
        cursor = page["nextCursor"]
    return out

cURL (una página; encadena manualmente con el nextCursor devuelto):

# Primera página
curl "https://api.dev.allsign.io/v3/documents?limit=100" \
  -H "Authorization: Bearer $ALLSIGN_KEY"

# Siguiente página: pega el nextCursor de la respuesta anterior
curl "https://api.dev.allsign.io/v3/documents?limit=100&startingAfter=djF8Y3JlYXRlZEF0fC4uLg" \
  -H "Authorization: Bearer $ALLSIGN_KEY"

Regla de oro: el loop termina cuando hasMore === false, no cuando llegas a un total. Así funciona aunque la colección crezca mientras paginas.

Conteo total

totalCount es null por default. Solo se calcula si pides includeTotal=true, y ese cálculo es caro (escanea la colección completa). Pídelo únicamente cuando de verdad necesites mostrar un total (p.ej. "1 284 documentos" en una UI), y nunca lo uses como condición para terminar tu loop de paginación — para eso está hasMore.

curl "https://api.dev.allsign.io/v3/documents?limit=20&includeTotal=true" \
  -H "Authorization: Bearer $ALLSIGN_KEY"
{
  "object": "list",
  "data": [ /* ... */ ],
  "hasMore": true,
  "nextCursor": "djF8Y3JlYXRlZEF0fC4uLg",
  "limit": 20,
  "totalCount": 1284
}