Skip to content

Brand API skill

A skill file that teaches an AI agent to manage Tarkle brand pages with the API, and when to use MCP instead.

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. Header Authorization: Bearer tk_....
  • Header X-Workspace-Key: <8 character key> on every call inside a workspace. GET /workspaces lists 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 is 404 not_found.
  • Add Idempotency-Key: <uuid> to every POST, PATCH, PUT and DELETE. A retry with the same key and body returns the first answer (Idempotent-Replayed: true). A reused key with a different body is 422 idempotency_key_reused.
  • Errors are application/problem+json. Branch on code, not on the message. On 429, wait Retry-After seconds.
  • 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

  1. POST /brands {"name": "Acme Bank", "slug": "acme-bank"}. Limits are the app’s: name 48 characters, summary 200, story 2,000. Do not send access, is_draft or confirm_public; a new brand is a private draft.
  2. PATCH /brands/{brand} with text, links, primary_color (#RRGGBB), download_approval, tracking, SEO fields.
  3. Upload files (below).
  4. 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.
  5. To open it to everyone, PATCH {"access": "public", "confirm_public": true}. Without the flag the call is 400 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)

  1. 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).
  2. PUT the bytes to upload.url. No Authorization header on this request.
  3. POST /brands/{brand}/files/{file_id}/complete. For logo you may send name, type, usage. For asset send collection_id or collection_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_file and is removed.
  • 409 file_not_uploaded: the PUT did not happen. 409 conflict on asset: 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).

Type to search…

↑↓ navigate↵ selectEsc close