This page is the text of the tarkle-brand-api agent skill. Give it to an assistant that manages Tarkle brand pages. Ask for this page with Accept: text/markdown to get it as Markdown. The skill file itself is SKILL.md with this front matter:
name: tarkle-brand-api
description: Create, update, publish, delete and restore Tarkle brand pages through the Tarkle API, including brand files, brand domains and brand webhooks. Use when a task manages a workspace's brand pages from code or from an agent, or when you must choose between the Tarkle MCP server and the API.Use this skill to manage Tarkle brand pages (logo, color, font and guideline pages that a workspace shares with clients) from code or an agent. Check facts in the live OpenAPI file before relying on this text: GET https://tarkle.com/api/v1/openapi.json needs no token and lists every path, scope and error code.
API or MCP?
| You hold | You can do |
|---|---|
An API token (tk_..., made in Settings > API) |
Everything in this skill. |
| An MCP token or OAuth login | Only tarkle_list_brands (read-only, takes a workspaceId uuid). It lists brands and their domains. No MCP tool creates, edits, uploads, deletes or reads a brand’s details. |
An MCP token does not work on /api/v1. If a task needs more than a list and you only have MCP, say so and ask for an API token. Never put a token in a prompt, a file in the repository or a log.
Every call
- Base URL
https://tarkle.com/api/v1. HeaderAuthorization: Bearer tk_.... - Header
X-Workspace-Key: <8 character key>on every call inside a workspace.GET /workspaceslists the keys the token can use. A workspace id is refused. - The token acts as the person who created it. For brand calls that person must be an owner or admin of the workspace, otherwise
403 forbidden. - A brand in
{brand}is its id (uuid) or its slug. A brand of another workspace is404 not_found. - Add
Idempotency-Key: <uuid>to everyPOST,PATCH,PUTandDELETE. A retry with the same key and body returns the first answer (Idempotent-Replayed: true). A reused key with a different body is422 idempotency_key_reused. - Errors are
application/problem+json. Branch oncode, not on the message. On429, waitRetry-Afterseconds. - Limits: 120 requests a minute per token on brand calls. Read
X-RateLimit-*instead of hard-coding it.
Scopes
A token with no scopes can do nothing. Ask for the smallest set.
| Scope | Calls |
|---|---|
brands:read |
GET /brands, /brands/{brand}, /assets, /markdown, /analytics |
brands:write |
PATCH /brands/{brand} |
brands:create |
POST /brands, DELETE /brands/{brand}, GET /deleted-brands, POST /deleted-brands/{brand}/restore |
brands:files |
POST /brands/{brand}/files, POST /brands/{brand}/files/{file}/complete |
brands:domains |
GET, PUT, DELETE /brands/{brand}/domain |
brands:requests |
GET /brands/{brand}/requests, POST /brands/{brand}/requests/{request} |
webhooks:manage |
/webhooks/** |
A 403 forbidden that names a scope means the token lacks it. Ask the user for a token that has it.
Recipes
Create a brand and fill it in
POST /brands {"name": "Acme Bank", "slug": "acme-bank"}. Limits are the app’s: name 48 characters, summary 200, story 2,000. Do not sendaccess,is_draftorconfirm_public; a new brand is a private draft.PATCH /brands/{brand}with text, links,primary_color(#RRGGBB),download_approval,tracking, SEO fields.- Upload files (below).
- To publish,
PATCH {"is_draft": false}. The API does not check what the editor requires before publishing (summary, story, website, icon, main logo, primary color, fonts, banner), so check the brand is complete first. - To open it to everyone,
PATCH {"access": "public", "confirm_public": true}. Without the flag the call is400 confirmation_required. This removes any password. Ask the user before doing it.access: "restricted"needs a contact or organization assigned in the app first.
Upload a file (two steps)
POST /brands/{brand}/files {"slot": "icon", "size": <bytes>, "filename": "icon.png"}. Slots:icon,main_logo,banner,social_image,favicon(replace),logo(extra logo),asset(collection file).PUTthe bytes toupload.url. No Authorization header on this request.POST /brands/{brand}/files/{file_id}/complete. Forlogoyou may sendname,type,usage. Forassetsendcollection_idorcollection_name.
- Limits: logos and icons PNG, JPG, WebP or SVG up to 10 MB; banner and social image PNG, JPG, WebP up to 10 MB; favicon also ICO up to 2 MB; collection files any type up to 100 MB. The type is read from the file’s bytes, not its name: a renamed program is
422 invalid_fileand is removed. 409 file_not_uploaded: the PUT did not happen.409 conflictonasset: the brand still has images from the older editor; ask the user to open it in the app once.- Completing twice is safe (
already_attached: true).
Connect a domain
PUT /brands/{brand}/domain {"domain": "brand.example.com"} (plain hostname). Show the user the returned dns_records to add at their DNS host, then poll GET /brands/{brand}/domain until status is active; do not poll faster than every 30 seconds. Free plans have no brand domain: 403 forbidden with details.entitlement: "BRAND_PAGE_DOMAIN_LIMIT". Tell the user it needs Pro; do not retry. 409 domain_taken means someone else uses it.
Delete and restore
DELETE /brands/{brand} with {"confirm_delete": true} moves the brand to the bin (30 days on Free, 90 on Pro and Agency). Its domains are detached and are not restored; requests, analytics and the kit link are not kept. Ask the user before deleting. GET /deleted-brands lists what can come back; POST /deleted-brands/{id}/restore restores it (409 name_taken: send another slug; 410 expired: gone; 403: the plan’s brand page limit).
Listen for changes
POST /webhooks {"url": "https://...", "events": ["brand.updated"]} returns the secret once. Verify Tarkle-Signature (t=<unix>,v1=<hex>, HMAC-SHA256 of <t>.<raw body>, reject t older than 5 minutes, accept any v1). Events: brand.updated, brand.downloaded, brand.request.created|approved|rejected, and brand.viewed only when named. Events never carry the visitor of a download.
Rules for agents
- Read before you write:
GET /brands/{brand}and show the user what will change. Send only the fields that change. - Confirm with the user before: making a brand public, deleting a brand, removing a domain, deleting a webhook.
- Plan limits answer with the app’s own errors. Report them; never try to work around a plan limit.
- Treat test data as real: use a clearly named test brand and delete it (to the bin) when done.
- Do not call the API from browser code. It has no CORS support and a token in a page is a leaked token.
- Not available in the API: setting a password, changing fonts, team or press, removing a file, uploading team photos, guideline images or fonts.
Where to read more
https://tarkle.com/docs/api/brands (guides), https://tarkle.com/docs/api/errors (one page per error code), https://tarkle.com/help/branding/what-is-a-brand-page (what a brand page is for customers).