# Despliega un Workload desde una imagen pública
Esta guía te lleva de cero a un Workload en marcha usando solo la API. Ejecuta `nginx` desde Docker Hub, pero cualquier imagen pública funciona igual.

El Workload se llamaba antes Container; los nombres de archivo de las guías conservan la palabra antigua.

## Antes de empezar

Necesitas una cuenta con una organización, saldo en la cartera u otra forma de pagar, y un token. Los ejemplos leen dos variables de entorno:

```bash
export ZAROZ_API="https://kernel.zaroz.cloud/api/v2"
export ZAROZ_TOKEN="<tu token de sesión o token de acceso personal>"
```

Cada petición envía `Authorization: Bearer $ZAROZ_TOKEN`. Los ejemplos de TypeScript necesitan Node 20 o posterior (ejecútalos con `npx tsx archivo.ts`); los de Python necesitan `pip install requests`. Todos usan este pequeño ayudante, que se muestra una sola vez:

**TypeScript**

```ts
const API = process.env.ZAROZ_API ?? "https://kernel.zaroz.cloud/api/v2";
const TOKEN = process.env.ZAROZ_TOKEN!;

export async function api(method: string, path: string, body?: unknown) {
  const res = await fetch(API + path, {
    method,
    headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${method} ${path} -> ${res.status} ${await res.text()}`);
  return res.status === 204 ? null : res.json();
}
```

**Python**

```python
import os, requests

API = os.environ.get("ZAROZ_API", "https://kernel.zaroz.cloud/api/v2")
S = requests.Session()
S.headers["Authorization"] = "Bearer " + os.environ["ZAROZ_TOKEN"]

def api(method, path, body=None, **params):
    r = S.request(method, API + path, json=body, params=params or None)
    r.raise_for_status()
    return None if r.status_code == 204 else r.json()
```

## 1. Encuentra el producto, un plan y una región

Lista el catálogo y elige el producto Workload por su `name`. Después pide sus planes en una región, y las regiones en las que puedes comprar.

```bash
curl -s "$ZAROZ_API/catalog/products?page_size=100" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '.data[] | {id, name, slug}'
curl -s "$ZAROZ_API/catalog/regions?product_id=$PRODUCT_ID" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '.data[] | {id, name, available}'
curl -s "$ZAROZ_API/catalog/products/$PRODUCT_ID/plans?region_id=$REGION_ID" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '.data[] | {id, name, price, available}'
```

```ts
import { api } from "./api";
const products = await api("GET", "/catalog/products?page_size=100");
const product = products.data.find((p: any) => /workload/i.test(p.name));
const regions = await api("GET", `/catalog/regions?product_id=${product.id}`);
const region = regions.data.find((r: any) => r.available);
const plans = await api("GET", `/catalog/products/${product.id}/plans?region_id=${region.id}`);
const plan = plans.data.find((p: any) => p.available);
```

```python
products = api("GET", "/catalog/products", page_size=100)
product = next(p for p in products["data"] if "workload" in p["name"].lower())
regions = api("GET", "/catalog/regions", product_id=product["id"])
region = next(r for r in regions["data"] if r["available"])
plans = api("GET", f"/catalog/products/{product['id']}/plans", region_id=region["id"])
plan = next(p for p in plans["data"] if p["available"])
```

`plan.price` es el precio mensual; la tarifa de unidad extra es ese precio dividido entre 720 (mira [Facturación](/es/units/billing)).

## 2. Cotiza la compra

Una cotización pone precio a la compra sin comprar nada. Envía el cuerpo de checkout sin `name` ni `slug`.

```bash
curl -s -X POST "$ZAROZ_API/checkout/quote" \
  -H "Authorization: Bearer $ZAROZ_TOKEN" -H "Content-Type: application/json" \
  -d "{\"product_id\":\"$PRODUCT_ID\",\"plan_id\":$PLAN_ID,\"region_id\":$REGION_ID}"
```

```ts
const quote = await api("POST", "/checkout/quote", {
  product_id: product.id, plan_id: plan.id, region_id: region.id,
});
console.log(quote.total_cents, quote.currency, quote.cadence_label);
```

```python
quote = api("POST", "/checkout/quote", {
    "product_id": product["id"], "plan_id": plan["id"], "region_id": region["id"],
})
print(quote["total_cents"], quote["currency"], quote["cadence_label"])
```

## 3. Cómpralo

El checkout crea una suscripción **pendiente** y la factura que la establece. `name` lo eliges tú. `slug` es el segmento de URL que identifica el servicio, único dentro de su proyecto; además se convierte en el [nombre privado](/es/units/private-names) del Workload.

```bash
curl -s -X POST "$ZAROZ_API/checkout" \
  -H "Authorization: Bearer $ZAROZ_TOKEN" -H "Content-Type: application/json" \
  -d "{\"product_id\":\"$PRODUCT_ID\",\"plan_id\":$PLAN_ID,\"region_id\":$REGION_ID,\"name\":\"Web\",\"slug\":\"web\"}"
```

```ts
const order = await api("POST", "/checkout", {
  product_id: product.id, plan_id: plan.id, region_id: region.id,
  name: "Web", slug: "web",
});
const subscriptionId = order.subscription.id;
const invoiceId = order.invoice.id;
```

```python
order = api("POST", "/checkout", {
    "product_id": product["id"], "plan_id": plan["id"], "region_id": region["id"],
    "name": "Web", "slug": "web",
})
subscription_id = order["subscription"]["id"]
invoice_id = order["invoice"]["id"]
```

Añade `organization_id` para facturar a una organización en lugar de a tu cuenta personal, y `project_id` para archivarlo en un proyecto concreto. Si omites `project_id`, se usa el proyecto por defecto de la organización.

## 4. Paga la factura

Desde la cartera:

```bash
curl -s -X POST "$ZAROZ_API/billing/invoices/$INVOICE_ID/pay-with-wallet" -H "Authorization: Bearer $ZAROZ_TOKEN"
```

```ts
await api("POST", `/billing/invoices/${invoiceId}/pay-with-wallet`);
```

```python
api("POST", f"/billing/invoices/{invoice_id}/pay-with-wallet")
```

Para pagar con tarjeta o PayPal, inicia el checkout de la factura (`/billing/invoices/{id}/checkout/mollie` o `.../paypal`) y sigue el enlace devuelto. Una vez pagada, la suscripción se activa y el aprovisionamiento empieza solo.

## 5. Encuentra la instancia de servicio

La suscripción es la compra; la **instancia de servicio** es lo que se ejecuta. Lee la suscripción para obtener su `project_id` y sus recursos para obtener el id de la instancia. Espera a que el estado sea `active`.

```bash
curl -s "$ZAROZ_API/subscriptions/$SUBSCRIPTION_ID" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '{status, project_id}'
curl -s "$ZAROZ_API/subscriptions/$SUBSCRIPTION_ID/resources" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '.service_instance.id'
```

```ts
const sub = await api("GET", `/subscriptions/${subscriptionId}`);   // espera a sub.status === "active"
const projectId = sub.project_id;
const res = await api("GET", `/subscriptions/${subscriptionId}/resources`);
const serviceId = res.service_instance.id;
```

```python
sub = api("GET", f"/subscriptions/{subscription_id}")   # espera a sub["status"] == "active"
project_id = sub["project_id"]
res = api("GET", f"/subscriptions/{subscription_id}/resources")
service_id = res["service_instance"]["id"]
```

Si el producto hace preguntas de configuración, `GET /service-instances/{id}/setup` lista los pasos sin responder, y responder el último inicia el primer aprovisionamiento (`PUT /service-instances/{id}/setup/steps/{step}`).

## 6. Indica qué imagen ejecutar

Guardar el origen no arranca nada; solo registra lo que descargará el próximo despliegue. Usa `kind: registry`. Para una imagen pública, no envíes `credentials`.

```bash
curl -s -X PUT "$ZAROZ_API/service-instances/$SERVICE_ID/source" \
  -H "Authorization: Bearer $ZAROZ_TOKEN" -H "Content-Type: application/json" \
  -d '{"kind":"registry","host":"docker.io","repository":"nginx","tag":"1.27"}'
```

```ts
const source = await api("PUT", `/service-instances/${serviceId}/source`, {
  kind: "registry", host: "docker.io", repository: "nginx", tag: "1.27",
});
console.log(source.image_reference);   // docker.io/library/nginx:1.27
```

```python
source = api("PUT", f"/service-instances/{service_id}/source", {
    "kind": "registry", "host": "docker.io", "repository": "nginx", "tag": "1.27",
})
print(source["image_reference"])
```

Zaroz completa con `library/` un nombre de un solo segmento de Docker Hub, así que `nginx` funciona. Usa una etiqueta concreta; la plataforma no vuelve a descargar cuando una etiqueta se mueve (`watch_tag` es siempre false).

## 7. Despliega

Un despliegue no lleva cuerpo. Envía el origen exactamente como está guardado.

```bash
curl -s -X POST "$ZAROZ_API/service-instances/$SERVICE_ID/deployments" -H "Authorization: Bearer $ZAROZ_TOKEN"
```

```ts
const deployment = await api("POST", `/service-instances/${serviceId}/deployments`);
// consulta hasta que se registre el resultado
for (;;) {
  const d = await api("GET", `/service-instances/${serviceId}/deployments/${deployment.id}`);
  if (d.outcome) { console.log(d.outcome, d.failure_reason); break; }
  await new Promise((r) => setTimeout(r, 3000));
}
```

```python
import time
deployment = api("POST", f"/service-instances/{service_id}/deployments")
while True:
    d = api("GET", f"/service-instances/{service_id}/deployments/{deployment['id']}")
    if d["outcome"]:
        print(d["outcome"], d.get("failure_reason")); break
    time.sleep(3)
```

`outcome` es `succeeded` o `failed`; `null` significa que aún no se ha registrado, nunca un fallo. Tu Workload ya ejecuta una unidad.

## Siguientes pasos

- [Añade un puerto público](/es/guides/containers-public-port) para que internet llegue a él.
- [Escálalo](/es/guides/containers-scale) a más de una unidad.
- [Lee sus logs](/es/guides/containers-logs).
