Skills
A written procedure, not a function — how your organization drafts an opinion, which sections a contract review has to cover.
A Skill is not a tool the model chooses to call — it is text you put in front of the model before it sees your first message, so it shapes the whole answer rather than arriving as the result of a call. Nothing here runs; there is no argument to decode and no result to send back. It works the way briefing a colleague works.
Authoring one
A Skill is a directory holding one file, SKILL.md: YAML front matter with a name and a description, then the instructions as Markdown for the rest of the file.
---
name: contract-review
description: How this organization reviews a vendor contract before signature.
---
## Sections to check, in order
1. Term and renewal — flag auto-renewal without a notice window.
2. Termination — who can end it, on what notice, at what cost.
3. Liability cap — flag anything uncapped or above policy.
4. Governing law and venue.
Write findings as a numbered list, one clause per line, quoting the
clause before the finding.SKILL.md file — once it's in place, any request can use it by name.Seeing what's available
/v1/skills{
"object": "list",
"data": [
{ "name": "contract-review",
"description": "How this organization reviews a vendor contract before signature.",
"chars": 612 }
]
}curl https://api.console.larsa.larsima.com/v1/skills \
-H "Authorization: Bearer $LARSA_API_KEY"import os
import requests
API = "https://api.console.larsa.larsima.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['LARSA_API_KEY']}"}
skills = requests.get(f"{API}/skills", headers=HEADERS).json()
for s in skills["data"]:
print(s["name"], "-", s["description"])const API = "https://api.console.larsa.larsima.com/v1";
const res = await fetch(`${API}/skills`, {
headers: { Authorization: `Bearer ${process.env.LARSA_API_KEY}` },
});
const skills = await res.json();
for (const s of skills.data) console.log(s.name, "-", s.description);const API = "https://api.console.larsa.larsima.com/v1";
const res = await fetch(`${API}/skills`, {
headers: { Authorization: `Bearer ${process.env.LARSA_API_KEY}` },
});
const skills = (await res.json()) as
{ data: { name: string; description: string }[] };
for (const s of skills.data) console.log(s.name, "-", s.description);package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET",
"https://api.console.larsa.larsima.com/v1/skills", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("LARSA_API_KEY"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
var out map[string]any
json.Unmarshal(raw, &out)
for _, s := range out["data"].([]any) {
row := s.(map[string]any)
fmt.Println(row["name"], "-", row["description"])
}
}use std::env;
fn main() {
let key = env::var("LARSA_API_KEY").unwrap();
let client = reqwest::blocking::Client::new();
let out: serde_json::Value = client
.get("https://api.console.larsa.larsima.com/v1/skills")
.bearer_auth(key).send().unwrap().json().unwrap();
for s in out["data"].as_array().unwrap() {
println!("{} - {}", s["name"], s["description"]);
}
}import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class ListSkills {
public static void main(String[] args) throws Exception {
HttpRequest req = HttpRequest.newBuilder(URI.create(
"https://api.console.larsa.larsima.com/v1/skills"))
.header("Authorization", "Bearer " + System.getenv("LARSA_API_KEY"))
.GET().build();
JSONObject out = new JSONObject(HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString()).body());
JSONArray data = out.getJSONArray("data");
for (int i = 0; i < data.length(); i++) {
JSONObject s = data.getJSONObject(i);
System.out.println(s.getString("name") + " - "
+ s.getString("description"));
}
}
}using System.Net.Http.Headers;
using System.Text.Json.Nodes;
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
Environment.GetEnvironmentVariable("LARSA_API_KEY"));
var skills = JsonNode.Parse(
await http.GetStringAsync("https://api.console.larsa.larsima.com/v1/skills"))!.AsObject();
foreach (var s in skills["data"]!.AsArray())
Console.WriteLine($"{s!["name"]} - {s["description"]}");<?php
$ch = curl_init("https://api.console.larsa.larsima.com/v1/skills");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("LARSA_API_KEY")],
]);
$skills = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($skills["data"] as $s) {
echo "{$s['name']} - {$s['description']}\n";
}require "json"
require "net/http"
require "uri"
uri = URI("https://api.console.larsa.larsima.com/v1/skills")
req = Net::HTTP::Get.new(uri, "Authorization" => "Bearer #{ENV.fetch('LARSA_API_KEY')}")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
JSON.parse(res.body)["data"].each do |s|
puts "#{s['name']} - #{s['description']}"
endUsing one
Name it on the request, alongside messages — not inside tools. Its instructions are prepended as a system message before every request that names it, so include it on every turn where you want it in effect; a multi-turn conversation does not remember it on its own.
{
"model": "larsa-auto",
"messages": [{"role": "user", "content": "…"}],
"skills": ["contract-review"]
}larsa-auto reads the skills field — that's the model the example below uses.curl https://api.console.larsa.larsima.com/v1/chat/completions \
-H "Authorization: Bearer $LARSA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "larsa-auto",
"messages": [{"role": "user", "content":
"Review clause 9: either party may terminate with 10 days written notice."}],
"skills": ["contract-review"]
}'import os
import requests
API = "https://api.console.larsa.larsima.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['LARSA_API_KEY']}",
"Content-Type": "application/json"}
res = requests.post(f"{API}/chat/completions", headers=HEADERS, json={
"model": "larsa-auto",
"messages": [{"role": "user", "content":
"Review clause 9: either party may terminate with "
"10 days written notice."}],
"skills": ["contract-review"],
})
print(res.json()["choices"][0]["message"]["content"])const API = "https://api.console.larsa.larsima.com/v1";
const res = await fetch(`${API}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LARSA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "larsa-auto",
messages: [{ role: "user", content:
"Review clause 9: either party may terminate with 10 " +
"days written notice." }],
skills: ["contract-review"],
}),
});
const out = await res.json();
console.log(out.choices[0].message.content);const API = "https://api.console.larsa.larsima.com/v1";
const res = await fetch(`${API}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LARSA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "larsa-auto",
messages: [{ role: "user", content:
"Review clause 9: either party may terminate with 10 " +
"days written notice." }],
skills: ["contract-review"],
}),
});
const out = (await res.json()) as any;
console.log(out.choices[0].message.content);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "larsa-auto",
"messages": []map[string]string{{"role": "user", "content":
"Review clause 9: either party may terminate with 10 " +
"days written notice."}},
"skills": []string{"contract-review"},
})
req, _ := http.NewRequest("POST",
"https://api.console.larsa.larsima.com/v1/chat/completions",
bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("LARSA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
var out map[string]any
json.Unmarshal(raw, &out)
choice := out["choices"].([]any)[0].(map[string]any)
fmt.Println(choice["message"].(map[string]any)["content"])
}use serde_json::json;
use std::env;
fn main() {
let key = env::var("LARSA_API_KEY").unwrap();
let client = reqwest::blocking::Client::new();
let out: serde_json::Value = client
.post("https://api.console.larsa.larsima.com/v1/chat/completions")
.bearer_auth(key)
.json(&json!({
"model": "larsa-auto",
"messages": [{"role": "user", "content":
"Review clause 9: either party may terminate with 10 days written notice."}],
"skills": ["contract-review"]
}))
.send().unwrap().json().unwrap();
println!("{}", out["choices"][0]["message"]["content"]);
}import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class UseSkill {
public static void main(String[] args) throws Exception {
JSONObject body = new JSONObject()
.put("model", "larsa-auto")
.put("messages", new JSONArray().put(new JSONObject()
.put("role", "user")
.put("content",
"Review clause 9: either party may terminate with 10 days written notice.")))
.put("skills", new JSONArray()
.put("contract-review"));
HttpRequest req = HttpRequest.newBuilder(URI.create(
"https://api.console.larsa.larsima.com/v1/chat/completions"))
.header("Authorization", "Bearer " + System.getenv("LARSA_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
.build();
HttpResponse<String> res = HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString());
JSONObject out = new JSONObject(res.body());
System.out.println(out.getJSONArray("choices")
.getJSONObject(0).getJSONObject("message")
.getString("content"));
}
}using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
Environment.GetEnvironmentVariable("LARSA_API_KEY"));
var body = new JsonObject {
["model"] = "larsa-auto",
["messages"] = new JsonArray {
new JsonObject { ["role"] = "user", ["content"] =
"Review clause 9: either party may terminate with 10 " +
"days written notice." },
},
["skills"] = new JsonArray { "contract-review" },
};
var res = await http.PostAsync(
"https://api.console.larsa.larsima.com/v1/chat/completions",
new StringContent(body.ToJsonString(), Encoding.UTF8,
"application/json"));
var out = JsonNode.Parse(
await res.Content.ReadAsStringAsync())!.AsObject();
Console.WriteLine(out["choices"]![0]!["message"]!["content"]);<?php
$ch = curl_init("https://api.console.larsa.larsima.com/v1/chat/completions");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("LARSA_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"model" => "larsa-auto",
"messages" => [["role" => "user", "content" =>
"Review clause 9: either party may terminate with 10 " .
"days written notice."]],
"skills" => ["contract-review"],
]),
]);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $out["choices"][0]["message"]["content"], "\n";require "json"
require "net/http"
require "uri"
uri = URI("https://api.console.larsa.larsima.com/v1/chat/completions")
req = Net::HTTP::Post.new(uri,
"Authorization" => "Bearer #{ENV.fetch('LARSA_API_KEY')}",
"Content-Type" => "application/json")
req.body = {
model: "larsa-auto",
messages: [{ role: "user", content:
"Review clause 9: either party may terminate with 10 "\
"days written notice." }],
skills: ["contract-review"],
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)["choices"][0]["message"]["content"]How the model decides to use it
It does not — you do, by naming it on the request. A Skill is never offered to the model as a choice the way a tool is; its instructions are already in the system prompt by the time the model sees your first message. If you want the model itself to pick among several Skills, that selection has to live in your own application code before you call this API — nothing here does it for you.