cURL
curl -X POST 'https://api.gumloop.com/api/v1/skills' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-F 'team_id=YOUR_TEAM_ID' \
-F 'files=@./lead-enrichment.skill;type=application/zip'from gumloop import Gumloop
client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
with open("lead-enrichment.skill", "rb") as fh:
response = client.skills.create(
files=[("lead-enrichment.skill", fh.read(), "application/zip")],
team_id="YOUR_TEAM_ID",
)
print(response.skill.id)const form = new FormData();
form.append('files', '<string>');
form.append('team_id', '<string>');
form.append('files.items', '{
"fileName": "example-file"
}');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.gumloop.com/api/v1/skills', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gumloop.com/api/v1/skills",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data; boundary=---011000010111000001101001"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.gumloop.com/api/v1/skills"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gumloop.com/api/v1/skills")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gumloop.com/api/v1/skills")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'multipart/form-data; boundary=---011000010111000001101001'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"skill": {
"id": "skill_x7y8z9",
"name": "Lead enrichment",
"description": "Enriches inbound leads with firmographics.",
"team_id": "team_4f8c92ab",
"created_at": "2026-05-19T09:00:00Z",
"updated_at": "2026-05-19T09:00:00Z",
"metadata": {},
"usage_count": 0,
"view_count": 0,
"last_used_at": null,
"version_id": "sv_a1b2c3d4",
"major_version": 1,
"is_deployed": false,
"version_created_at": "2026-05-19T09:00:00Z",
"creator": {
"id": "user_2b9d71f0",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"profile_picture": null
}
}
}Create skill
Upload a skill package and create a new skill. The package must include a SKILL.md with name and description frontmatter; uploads may be a single .md file (stored as SKILL.md), or a .zip / .skill archive containing SKILL.md at its root. The initial version is created automatically. Maximum upload size is 10 MB.
POST
/
skills
cURL
curl -X POST 'https://api.gumloop.com/api/v1/skills' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-F 'team_id=YOUR_TEAM_ID' \
-F 'files=@./lead-enrichment.skill;type=application/zip'from gumloop import Gumloop
client = Gumloop(access_token="YOUR_ACCESS_TOKEN")
with open("lead-enrichment.skill", "rb") as fh:
response = client.skills.create(
files=[("lead-enrichment.skill", fh.read(), "application/zip")],
team_id="YOUR_TEAM_ID",
)
print(response.skill.id)const form = new FormData();
form.append('files', '<string>');
form.append('team_id', '<string>');
form.append('files.items', '{
"fileName": "example-file"
}');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.gumloop.com/api/v1/skills', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gumloop.com/api/v1/skills",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data; boundary=---011000010111000001101001"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.gumloop.com/api/v1/skills"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gumloop.com/api/v1/skills")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gumloop.com/api/v1/skills")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'multipart/form-data; boundary=---011000010111000001101001'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"team_id\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"files.items\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n{\r\n \"fileName\": \"example-file\"\r\n}\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"skill": {
"id": "skill_x7y8z9",
"name": "Lead enrichment",
"description": "Enriches inbound leads with firmographics.",
"team_id": "team_4f8c92ab",
"created_at": "2026-05-19T09:00:00Z",
"updated_at": "2026-05-19T09:00:00Z",
"metadata": {},
"usage_count": 0,
"view_count": 0,
"last_used_at": null,
"version_id": "sv_a1b2c3d4",
"major_version": 1,
"is_deployed": false,
"version_created_at": "2026-05-19T09:00:00Z",
"creator": {
"id": "user_2b9d71f0",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"profile_picture": null
}
}
}Authorizations
Body
multipart/form-data
One or more file parts. Accepts a single .md file (stored as SKILL.md), a .zip / .skill archive containing SKILL.md at its root, or loose files that together include a SKILL.md. Total upload must not exceed 10 MB.
Team that should own the skill. When omitted, the skill is owned by the authenticated user.
Response
Skill created.
Show child attributes
Show child attributes
Was this page helpful?
⌘I
