Ir al contenido

Buscar en la documentación

Busca en la documentación de Skiffly

API pública

Un único endpoint GraphQL para todo lo que hacen el panel, la CLI y el servidor MCP. Tipos de token, límites de tasa, operaciones comunes y compatibilidad con Railway.

POST https://api.skiffly.dev/graphql        (alias: /graphql/v2)
Authorization: Bearer <token>
Content-Type: application/json

GraphiQL está disponible en la misma URL desde un navegador. El esquema sigue la API pública de Railway: conexiones Relay, objetos input, DeploymentStatus en mayúsculas, variables como un mapa, así que los documentos escritos para Railway suelen ejecutarse sin cambios.

Tokens#

TokenEncabezadoAlcanceCrear
CuentaAuthorization: Bearer <session token>todos los espacios de trabajo del usuarioskiffly login guarda uno
Espacio de trabajoAuthorization: Bearer skiffly_…un espacio de trabajo; scope read o writeConfiguración → Desarrollador → Crear token, o apiTokenCreate(input: { name, workspaceId, scopes })
ProyectoProject-Access-Token: skiffly_project_…un entorno de un proyecto: desplegar, variables, logs, dominios, volúmenesprojectTokenCreate(input: { projectId, environmentId, name })

Los tokens se muestran una sola vez. Los tokens de proyecto son la opción correcta para CI: no pueden ver otros proyectos ni la facturación. Cualquier cosa fuera del alcance del token devuelve FORBIDDEN.

Límites de tasa por plan del espacio de trabajo: Free 100 solicitudes/hora; Hobby 1 000/hora y 10/segundo; Pro 10 000/hora y 50/segundo. Las respuestas incluyen X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset; un 429 incluye Retry-After.

Un helper para el shell#

export SKIFFLY_TOKEN=skiffly_...
gql() { curl -s https://api.skiffly.dev/graphql -H "Authorization: Bearer $SKIFFLY_TOKEN" -H 'content-type: application/json' \
  --data-binary "$(node -e 'const [q,v]=process.argv.slice(1);process.stdout.write(JSON.stringify({query:q,variables:JSON.parse(v||"{}")}))' "$1" "$2")"; echo; }
 
gql '{ me { login workspaces { id name role } } }'

La CLI hace lo mismo con skiffly api '<query>' -v '<json>' usando su token guardado.

Recorrido: proyecto → servicio → dominio#

gql 'mutation($ws:String!){ projectCreate(input:{workspaceId:$ws, name:"wevo"}){ id environments { edges { node { id name } } } } }' '{"ws":"<workspaceId>"}'
 
# repositorio (Railpack, o el Dockerfile en la raíz)
gql 'mutation{ serviceCreate(input:{projectId:"<projectId>", name:"web", source:{repo:"acme/web"}, branch:"main"}){ id } }'
# imagen, con variables
gql 'mutation{ serviceCreate(input:{projectId:"<projectId>", name:"redis", source:{image:"redis:7"}, port:6379, variables:{ REDIS_ARGS:"--save 60 1" }}){ id } }'
 
# variables (serviceId null = compartidas); los valores pueden referenciar otros servicios
gql 'mutation{ variableCollectionUpsert(input:{projectId:"<projectId>", environmentId:"<envId>", serviceId:"<serviceId>", variables:{ REDIS_URL:"${{redis.REDIS_URL}}" }}) }'
gql '{ variables(projectId:"<projectId>", environmentId:"<envId>", serviceId:"<serviceId>") }'
 
# dominios
gql 'mutation{ serviceDomainCreate(input:{serviceId:"<serviceId>", environmentId:"<envId>"}){ domain } }'
gql 'mutation{ customDomainCreate(input:{projectId:"<projectId>", serviceId:"<serviceId>", environmentId:"<envId>", domain:"www.example.com"}){ id status { dnsRecords { recordType fqdn requiredValue } } } }'
gql 'mutation{ customDomainVerify(id:"<domainId>"){ status { verified } } }'
 
# volumen, configuración, despliegue, logs
gql 'mutation{ volumeCreate(input:{projectId:"<projectId>", serviceId:"<serviceId>", environmentId:"<envId>", mountPath:"/data", sizeGb:5}){ id } }'
gql 'mutation{ serviceInstanceUpdate(serviceId:"<serviceId>", environmentId:"<envId>", input:{ memoryBytes:1073741824, cpuMillis:1000, healthcheckPath:"/healthz", numReplicas:1 }) }'
gql 'mutation{ serviceInstanceDeployV2(serviceId:"<serviceId>", environmentId:"<envId>") }'
gql '{ deployments(input:{serviceId:"<serviceId>", environmentId:"<envId>"}, first:3){ edges { node { id status message createdAt } } } }'
gql '{ deploymentLogs(deploymentId:"<deploymentId>", limit:200){ timestamp severity message } }'

serviceInstanceDeployV2 devuelve el id del despliegue; consulta deployment(id) { status message } hasta que sea SUCCESS o FAILED.

Errores#

Los errores de GraphQL llevan extensions.code: UNAUTHENTICATED, FORBIDDEN, NOT_FOUND, BAD_USER_INPUT, CONFLICT, PLAN_LIMIT (con el límite alcanzado), TEMPLATE_VARIABLES_REQUIRED (con extensions.missing), RATE_LIMITED.

Compatibilidad con Railway#

Mismos nombres y formas donde la semántica coincide: project, projects, environment(s), service, serviceInstance, deployment(s), deploymentLogs, environmentLogs, variables, domains, customDomain*, serviceDomain*, volume*, projectToken*, apiToken*, templates, template, templateDeployV2, serviceInstanceUpdate, serviceInstanceDeployV2, serviceInstanceRedeploy, variableUpsert, variableCollectionUpsert, variableDelete, deploymentCancel, deploymentRedeploy, deploymentRestart, deploymentRollback, tcpProxy*. Los argumentos son ID!, pero se aceptan documentos que declaran $id: String!.

Diferencias que importan:

RailwaySkiffly
buildLogs y deploymentLogsun solo deploymentLogs; Log.stream es build, runtime o system
deploymentRestart(id)también serviceRestart(serviceId, environmentId)
serviceConnect para adjuntar un origenserviceUpdate(id, { source })
los recursos se definen a nivel de planserviceInstanceUpdate acepta cpuMillis / memoryBytes
objeto Workspace.planenum Plan FREE / HOBBY / PRO
Subscriptions para los logssolo HTTP; consulta deploymentLogs periódicamente
Cambios preparados (environmentStageChanges, environmentPatchCommit*)no existen: los cambios se aplican de inmediato; vuelve a desplegar cuando haga falta
Regiones, gateways de salida, redes privadas, buckets, funciones, sandboxes, equipos/RBAC, feature flags, webhooksno disponibles

Adiciones exclusivas de Skiffly: billingAccount, ledger, payments, topupCreate, usage, serviceMetrics, nodes, githubInstallations, githubApp, tcpProxies, Service.internalHost, ServiceInstance.branch, ServiceUpdateInput.runtimeClass.

El SDL completo se sirve por introspección en el endpoint; la referencia de GraphQL lista todos los campos raíz.