Skills Quickstart
This guide takes a server-side developer from API key to a completed skill invocation. The examples use placeholder input and public-safe identifiers only. Keep API keys in your secret manager, shell profile, or deployment environment, and call the Skills API from a trusted server boundary.
Base URL: https://agents.uselovelace.com
Required scopes:
| Scope | Used for |
|---|---|
skills:read | Browse reusable skills, inspect generation jobs, list versions, and poll invocation status. |
skills:write | Create a generation job when the library cannot satisfy the request. |
skills:execute | Invoke the selected skill version. |
1. Start From An API Key
Create a developer API key from API keys with the scopes above. A
developer key has the sk_live_ prefix. The commands below expect the key to be
available as LOVELACE_API_KEY.
Send the key in the x-api-key header on every Skills request:
-H "x-api-key: $LOVELACE_API_KEY"
The Authorization: Bearer header is reserved for short-lived JWT session
tokens. A developer API key is not a JWT — sending it as a bearer token fails
authentication. Use x-api-key for the sk_live_ key.
Do not paste real keys into source files, shell history, issue comments, support tickets, or generated examples.
2. Preflight Reuse Before Generation
Ask the library whether an approved visible skill can satisfy the capability before creating a generation job.
curl -sS https://agents.uselovelace.com/api/v1/tool-generation-jobs/preflight \
-H "x-api-key: $LOVELACE_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"description": "Normalize invoice CSV rows into accounting import records",
"capabilities": ["csv_normalization", "accounting_import"],
"category": "business_operations",
"billingMode": "private_generation",
"inputSchema": {
"type": "object",
"properties": {
"csvText": { "type": "string" }
},
"required": ["csvText"]
},
"outputSchema": {
"type": "object",
"properties": {
"records": { "type": "array" }
},
"required": ["records"]
}
}'
If the response has state: "reuse" and includes matchedSkill, use
matchedSkill.autoToolId and matchedSkill.defaultVersionId for invocation.
If the response has state: "recommend", show the recommendation to the user
or operator before spending generation quota. If the response has
state: "synthesize", continue to generation.
3. Create A Generation Job When Reuse Does Not Fit
curl -sS https://agents.uselovelace.com/api/v1/tool-generation-jobs \
-H "x-api-key: $LOVELACE_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"description": "Normalize invoice CSV rows into accounting import records",
"capabilities": ["csv_normalization", "accounting_import"],
"category": "business_operations",
"billingMode": "private_generation",
"permissions": {},
"inputSchema": {
"type": "object",
"properties": {
"csvText": { "type": "string" }
},
"required": ["csvText"]
},
"outputSchema": {
"type": "object",
"properties": {
"records": { "type": "array" }
},
"required": ["records"]
}
}'
The accepted response includes:
{
"id": "job_123",
"status": "queued",
"statusUrl": "/api/v1/tool-generation-jobs/job_123",
"pricingPreview": {
"billingMode": "private_generation",
"unitMetric": "skill_generations"
}
}
4. Poll The Job With A Bound
Poll statusUrl until the job is ready, failed, or canceled. Use a
bounded loop, backoff, and a timeout in your worker instead of an unbounded
client-side interval.
curl -sS https://agents.uselovelace.com/api/v1/tool-generation-jobs/job_123 \
-H "x-api-key: $LOVELACE_API_KEY"
A ready job includes toolId, versionId, and toolUrl:
{
"id": "job_123",
"toolId": "tool_invoice_normalizer",
"versionId": "ver_20260614",
"status": "ready",
"toolUrl": "/api/v1/tools/tool_invoice_normalizer"
}
If the job fails, handle failureCategory and failureMessage as public-safe
diagnostics. Do not log prompts, generated source, validation payloads, or
private customer records.
5. Select A Validated Version
List versions and pick a validated selectable version. Use latest_selectable
for normal traffic and pinned_selectable when you need a stable retained
version.
curl -sS "https://agents.uselovelace.com/api/v1/tools/tool_invoice_normalizer/versions?limit=25&offset=0" \
-H "x-api-key: $LOVELACE_API_KEY"
Use the version string in the invocation request when you want to pin a
specific version. Omit version to use the current latest selectable version.
6. Invoke The Skill
curl -sS https://agents.uselovelace.com/api/v1/tool-invocations \
-H "x-api-key: $LOVELACE_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"toolId": "tool_invoice_normalizer",
"version": "1.0.0",
"input": {
"csvText": "invoice_id,total,currency\nINV-100,42.00,USD"
},
"metadata": {
"requestKind": "quickstart_example"
}
}'
The response includes an invocation status URL:
{
"invocationId": "inv_123",
"toolId": "tool_invoice_normalizer",
"status": "pending",
"statusUrl": "/api/v1/tool-invocations/inv_123"
}
Poll or long-poll the invocation with a timeout:
curl -sS "https://agents.uselovelace.com/api/v1/tool-invocations/inv_123/wait?timeoutMs=5000" \
-H "x-api-key: $LOVELACE_API_KEY"
Completed invocations return status: "completed" and an output object.
Failed invocations return status: "failed", failureCategory, and a safe
failureMessage.
7. Handle Typed Failures
Branch on error and details.category, not on message text.
type SkillsErrorBody = {
readonly error: string;
readonly details?: {
readonly category?: string;
readonly retryable?: boolean;
readonly dimension?: string;
readonly current?: number;
readonly limit?: number;
};
};
function shouldRetrySkillsRequest(error: SkillsErrorBody): boolean {
return error.details?.retryable === true;
}
function describeSkillsFailure(error: SkillsErrorBody): string {
switch (error.error) {
case "authorization_failed":
return "The request is not authorized for this Skills operation.";
case "invalid_request":
return "The request shape is invalid.";
case "quota_exceeded":
return `Quota exhausted for ${error.details?.dimension ?? "Skills"}.`;
case "credit_pending":
return "A reusable-skill credit is pending settlement.";
case "billing_required":
return "The account owner needs to resolve billing.";
case "billing_unavailable":
return "Billing state is temporarily unavailable.";
case "learning_consent_required":
return "Learning-enabled generation needs account consent.";
case "unavailable_version":
return "The requested skill version is unavailable.";
case "validation_error":
return "The candidate failed validation.";
case "execution_failed":
return "The protected execution boundary failed.";
default:
return "The Skills request failed with a typed public error.";
}
}
Use bounded retries only when details.retryable is true. See
troubleshooting for action guidance and
billing for quota and credit behavior.