Acceso y consumo
Conecta los límites de tus planes a las acciones de tu aplicación.
Define una cuota y asígnala a un plan
await billete.features.create({
key: "tokens",
name: "Tokens",
type: "metered",
unit: "tokens",
reset: { every: "billing" },
});
await billete.plans.create({
name: "Pro",
currency: "USD",
prices: [{ months: 1, amount: 1000 }],
features: { tokens: 100_000 },
});El cliente obtiene las cuotas a través de su suscripción. customer admite el ID de Billete o tu externalId. Una cuota se identifica por su clave (features.retrieve("tokens"), features.check("user_123", "tokens")) o por su ID feat_, en todos los métodos.
Consume antes de ejecutar
const usage = await billete.features.consume(
"user_123",
"tokens",
850,
{ idempotencyKey: "tokens-job-example-001" },
);
if (!usage.allowed) {
throw new Error("No tienes cuota suficiente para esta operación");
}La operación es atómica: si supera el límite, no consume nada. La idempotencia de la petición evita repetir el consumo; tu propio trabajo también debe ejecutarse una sola vez.
Otras operaciones
const access = await billete.features.check("user_123", "tokens");
const enabled = await billete.features.isEnabled("user_123", "export-pdf");
const features = await billete.features.listForCustomer("user_123");Solo las cuotas count permiten release(customer, key, amount, options) y set(customer, key, value, options). Para metered, registra una operación: toda devolución vuelve al periodo original, aunque ya haya empezado otro.
Reservar trabajos y confirmar su consumo real
const { allowed, operation } = await billete.features.reserve("org_123", "tokens", 40, {
member: "user_42", operation: job.id, expiresInSeconds: 600,
});
if (!allowed || !operation) return quotaExceeded();
if (operation.status !== "reserved") return alreadyHandled(operation);
const result = await generate().catch(async (error: unknown) => {
await billete.features.operations.release(operation.id);
throw error;
});
await billete.features.operations.commit(operation.id, { amount: result.tokensUsed });Tu cola debe garantizar que solo un worker ejecute cada trabajo. Billete evita descontar cuota dos veces; no ejecuta ni bloquea tus trabajos. Elige un vencimiento mayor que su duración máxima.
Una respuesta de confirmación perdida se reintenta con el mismo monto o se consulta con features.operations.retrieve(id). Nunca liberes por una duda de red: si está committed, ya quedó; si sigue reserved, reintenta. operation_expired significa que el trabajo terminó sin reserva: tu aplicación decide si lo entrega y registra el consumo o lo descarta.
features.operations.release(id) libera una reserva; features.operations.refund(id) devuelve consumo confirmado. Ambas son idempotentes. Para consumo inmediato idempotente usa consume(customer, key, amount, { operation: job.id, member }). Las operaciones pueden cerrarse después de quitar al miembro o cambiar de plan.
Al crear una cuota, per: "member" mantiene un contador separado por miembro. Pasa { member: "user_42" } a check, consume, set o listForCustomer. Las consultas sin miembro muestran capacidades del cliente; los contadores individuales son null.