Sub2API Admin
Wei-Shaw/sub2api
Manages a Sub2API deployment from the command line: accounts, redeem and invitation codes, groups, proxies, imports, exports and raw admin API calls.
Ingest Podium webchat messages in production and survive the webchat-side failures — invalid phone formats accepted at the widget, contact auto-creation races producing duplicate records, session…
$ npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install jeremylongshore/tons-of-skills-marketplace podium-webchat-handler --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/jeremylongshore/tons-of-skills-marketplace.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/.curated/podium-webchat-handler .claude/skills/podium-webchat-handler && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "podium-webchat-handler" agent skill from https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/main/skills/.curated/podium-webchat-handler into .claude/skills/podium-webchat-handler/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "podium-webchat-handler", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/main/skills/.curated/podium-webchat-handlerType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install jeremylongshore/tons-of-skills-marketplace podium-webchat-handler --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jeremylongshore/tons-of-skills-marketplace.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/.curated/podium-webchat-handler .agents/skills/podium-webchat-handler && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "podium-webchat-handler" agent skill from https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/main/skills/.curated/podium-webchat-handler into .agents/skills/podium-webchat-handler/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "podium-webchat-handler", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install jeremylongshore/tons-of-skills-marketplace podium-webchat-handler --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jeremylongshore/tons-of-skills-marketplace.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/.curated/podium-webchat-handler .cursor/skills/podium-webchat-handler && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "podium-webchat-handler" agent skill from https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/main/skills/.curated/podium-webchat-handler into .cursor/skills/podium-webchat-handler/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "podium-webchat-handler", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/jeremylongshore/tons-of-skills-marketplace.git --path skills/.curated/podium-webchat-handler--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install jeremylongshore/tons-of-skills-marketplace podium-webchat-handler --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jeremylongshore/tons-of-skills-marketplace.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/.curated/podium-webchat-handler .gemini/skills/podium-webchat-handler && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "podium-webchat-handler" agent skill from https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/main/skills/.curated/podium-webchat-handler into .gemini/skills/podium-webchat-handler/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "podium-webchat-handler", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install jeremylongshore/tons-of-skills-marketplace podium-webchat-handlerInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/jeremylongshore/tons-of-skills-marketplace.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/.curated/podium-webchat-handler .github/skills/podium-webchat-handler && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "podium-webchat-handler" agent skill from https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/main/skills/.curated/podium-webchat-handler into .github/skills/podium-webchat-handler/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "podium-webchat-handler", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install jeremylongshore/tons-of-skills-marketplace podium-webchat-handler --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jeremylongshore/tons-of-skills-marketplace.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/.curated/podium-webchat-handler .opencode/skills/podium-webchat-handler && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "podium-webchat-handler" agent skill from https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/main/skills/.curated/podium-webchat-handler into .opencode/skills/podium-webchat-handler/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "podium-webchat-handler", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
podium-webchat-handlerIngest Podium webchat messages in production and survive the webchat-side failures — invalid phone formats accepted at the widget, contact auto-creation races producing duplicate records, session…
Podium Webchat Handler is an agent skill from jeremylongshore/tons-of-skills-marketplace. Ingest Podium webchat messages in production and survive the webchat-side failures — invalid phone formats accepted at the widget, contact auto-creation races producing duplicate records, session timeouts mid-conversation, attachment size overflows, cross-location chat routing wrong, and opt-out propagation lag. Use when hardening a webchat → API integration, building a multi-location chat widget, debugging duplicate contacts, or recovering from a cross-location routing incident. Trigger with "podium webchat"…
Its SKILL.md is about 4.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 13 other files, including scripts and reference files (for example `ARD.md`, `PRD.md` and `config/settings.yaml`). Compatibility notes: Designed for Claude Code
It sits in Backend & APIs, covering Third-party API integration. The repository describes itself as: Model-agnostic agent-skills platform with a harness-free canonical layer, verified adapters, and the ccpi package manager. Explore at tonsofskills.com. The licence is MIT.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit cfae287. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
ReadWriteEditBash(curl:*)Bash(jq:*)Bash(python3:*)GrepFrom allowed-tools in the SKILL.md frontmatter.
Ships 4 files in scripts/ (Python), which the agent can run.
Shell commands in SKILL.md call:
python3pipFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
api.podium.comAlso links to:
docs.podium.comgithub.compypi.orgFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Designed for Claude Code
From compatibility in the SKILL.md frontmatter.
Podium Webchat Handler loads about 4.7k tokens when it runs, and up to ~12k if it reads all its reference files. Until then it costs about 171 tokens; SKILL.md has 1,314 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from jeremylongshore/tons-of-skills-marketplace at commit cfae287, republished under its MIT licence (© jeremylongshore). 1,314 words, ~4,664 tokens.
.claude/skills/podium-webchat-handler/SKILL.md (or your agent's skills folder). This skill also uses 10 other files; get the full folder from GitHub.Ingest Podium webchat messages into your production system and operate the webchat layer when it breaks. This is not a setup walkthrough — it is the handler code your integration runs at 11am on a Saturday when a Brisbane customer's webchat lands on the Sydney store's queue, when two simultaneous webchats from the same phone produce two duplicate contact records, when a customer types 1 of a 1-2-3 answer and the session dies before they finish, and when a customer types STOP and the next session five minutes later still tries to SMS them.
The six production failures this skill prevents:
0412 345 678 (Australian local) or (415) 555-1234 (US local) without E.164 normalization. The handler stores the local form. The later SMS reply attempt fails silently because Podium expects +61412345678 / +14155551234. The agent thinks they replied; the customer never receives anything.1 of a 1-2-3 multiple-choice answer, walks away to grab lunch, comes back to finish, and discovers the agent picked up the conversation in fresh context without the 1 they sent.location_uid on the initial message. Every chat lands in the default location's queue regardless of which store the customer was actually browsing.podium-auth skill installed and a working PodiumAuth instance for OAuth token managementpodium-webhook-reliability skill installed if consuming webchat events via webhook (HMAC + dedup live there)phonenumbers library (Google's libphonenumber port): pip install phonenumberslocation_uid listBuild in this order. Each section neutralizes one production failure mode.
Normalize phone numbers to E.164 at the widget input boundary before the message ever reaches your API. The widget knows the customer's locale context; the API does not. Use phonenumbers for the parse + validation:
import phonenumbers
from phonenumbers import NumberParseException, PhoneNumberFormat, is_valid_number
class PhoneValidationError(Exception):
pass
def normalize_phone(raw: str, default_country: str = "AU") -> str:
"""Parse a raw phone string and return E.164 form. Raises on invalid."""
try:
parsed = phonenumbers.parse(raw, default_country)
except NumberParseException as e:
raise PhoneValidationError(f"unparseable phone {raw!r}: {e}")
if not is_valid_number(parsed):
raise PhoneValidationError(f"invalid phone for region {default_country}: {raw!r}")
return phonenumbers.format_number(parsed, PhoneNumberFormat.E164)
# Examples
assert normalize_phone("0412 345 678", "AU") == "+61412345678"
assert normalize_phone("(415) 555-1234", "US") == "+14155551234"
assert normalize_phone("+61 412 345 678", "AU") == "+61412345678"The default_country parameter is the location's country (Sydney → AU, Burleigh Heads → AU, San Francisco → US). Pass it from the widget context, never hardcode globally. If the widget runs on a multi-region site and cannot determine the default, fail closed — refuse to accept the message until the customer enters a +-prefixed number explicitly.
The naive pattern — if not contact_exists(phone): create_contact(phone) — has a TOCTOU race. Under simultaneous webchat arrivals from the same phone, both branches see "no" and both create. The fix is idempotent upsert keyed on the E.164 phone with a unique index in the contact store, and retry-on-conflict semantics:
import httpx
from podium_auth import PodiumAuth
async def upsert_contact_by_phone(
auth: PodiumAuth,
phone_e164: str,
location_uid: str,
first_name: str | None = None,
last_name: str | None = None,
) -> dict:
"""Idempotent contact creation. Returns the contact record; never creates a duplicate."""
token = await auth.get_token()
headers = {"Authorization": f"Bearer {token}"}
# Step 1: lookup by phone
async with httpx.AsyncClient(timeout=10) as c:
r = await c.get(
"https://api.podium.com/v4/contacts",
headers=headers,
params={"phone": phone_e164, "location_uid": location_uid},
)
if r.status_code == 200 and r.json().get("data"):
return r.json()["data"][0]
# Step 2: create — but tolerate 409 conflict from a racing creator
async with httpx.AsyncClient(timeout=10) as c:
r = await c.post(
"https://api.podium.com/v4/contacts",
headers=headers,
json={
"phone": phone_e164,
"location_uid": location_uid,
"first_name": first_name,
"last_name": last_name,
},
)
if r.status_code in (200, 201):
return r.json()
if r.status_code == 409:
# The race lost — refetch and return the winner's record
async with httpx.AsyncClient(timeout=10) as c:
r2 = await c.get(
"https://api.podium.com/v4/contacts",
headers=headers,
params={"phone": phone_e164, "location_uid": location_uid},
)
if r2.status_code == 200 and r2.json().get("data"):
return r2.json()["data"][0]
raise WebchatError(f"contact upsert failed: {r.status_code} {r.text}")
class WebchatError(Exception):
passIn your local contact mirror (if you maintain one), enforce a database-level unique index on phone_e164 so a parallel writer hits the constraint instead of silently double-inserting. The deeper mechanics — collision resolution when the same phone owns conflicting first/last names across sources — live in podium-contact-dedup. This skill prevents the most common race; that skill handles the harder reconciliation cases.
Podium webchat sessions have a server-side idle timeout. Detect approaching-expiry on your side and either prompt the customer to confirm they're still there, or buffer the partial answer so the agent picks up the conversation with context preserved:
import time
from dataclasses import dataclass, field
SESSION_IDLE_WARN_SECONDS = 20 * 60 # 20 min — prompt customer
SESSION_IDLE_CLOSE_SECONDS = 28 * 60 # 28 min — close cleanly before Podium expires
@dataclass
class WebchatSession:
session_uid: str
phone_e164: str
location_uid: str
last_message_at: float
partial_state: dict = field(default_factory=dict) # buffered multi-step answers
def idle_seconds(self) -> float:
return time.time() - self.last_message_at
def status(self) -> str:
idle = self.idle_seconds()
if idle >= SESSION_IDLE_CLOSE_SECONDS: return "close"
if idle >= SESSION_IDLE_WARN_SECONDS: return "warn"
return "active"
async def scan_sessions(sessions: dict[str, WebchatSession]) -> None:
"""Run on a 60s loop. Emit prompts and clean closures."""
for uid, s in list(sessions.items()):
st = s.status()
if st == "warn":
await send_keepalive_prompt(s.session_uid) # "still there? type anything to continue"
elif st == "close":
await persist_partial_state(s) # save the `1` of `1-2-3` answer
await close_session_cleanly(s.session_uid)
del sessions[uid]The buffered partial_state is the load-bearing piece. When the next message arrives on the same phone_e164 + location_uid, hydrate the previous partial state so the customer is not asked to start over.
Podium's 25MB attachment limit is documented but the API only returns the 413 after the upload completes. Validate at the widget — before the upload starts — so the customer is told immediately:
PODIUM_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024 # 25 MiB
class AttachmentTooLargeError(Exception):
pass
def validate_attachment_size(size_bytes: int) -> None:
if size_bytes > PODIUM_ATTACHMENT_MAX_BYTES:
raise AttachmentTooLargeError(
f"attachment is {size_bytes / 1024 / 1024:.1f} MiB; "
f"Podium accepts up to {PODIUM_ATTACHMENT_MAX_BYTES / 1024 / 1024:.0f} MiB"
)In the widget, wire this to the file-input change event. In the API handler, double-check the Content-Length of incoming uploads and reject with a 413 of your own before forwarding to Podium — this saves both the egress cost and the user-visible failure when the upload finishes and then dies.
A multi-location org needs location_uid on every webchat-originated request. The widget must know which location it represents — either via a per-location embed snippet or via a URL/cookie hint resolved at chat-open time. The handler must reject messages that arrive without a valid location_uid:
VALID_LOCATION_UIDS: set[str] = set() # populate at startup from Podium /v4/locations
async def load_locations(auth: PodiumAuth) -> None:
token = await auth.get_token()
async with httpx.AsyncClient(timeout=10) as c:
r = await c.get(
"https://api.podium.com/v4/locations",
headers={"Authorization": f"Bearer {token}"},
)
r.raise_for_status()
VALID_LOCATION_UIDS.clear()
VALID_LOCATION_UIDS.update(loc["uid"] for loc in r.json()["data"])
def validate_location(location_uid: str | None) -> str:
if not location_uid:
raise WebchatError("location_uid is required — refusing to route to a default")
if location_uid not in VALID_LOCATION_UIDS:
raise WebchatError(f"unknown location_uid {location_uid!r}")
return location_uidThe "Sydney store gets a Brisbane customer" failure mode happens specifically when the integration falls back to a default location on missing location_uid. Do not have a default. Refuse the request and surface a config error to the widget operator.
A STOP message in either channel must propagate to both. Maintain a single opt-out store keyed on E.164 phone, and consult it on every outbound message attempt regardless of channel:
OPTOUT_KEYWORDS = {"STOP", "UNSUBSCRIBE", "QUIT", "END", "CANCEL", "OPTOUT"}
async def check_optout(phone_e164: str) -> bool:
"""Returns True if this phone is opted out across ALL channels."""
# Backed by a database table or KV store; this is the unified view.
return await optout_store.is_opted_out(phone_e164)
async def record_optout(phone_e164: str, source_channel: str) -> None:
"""Called from BOTH the SMS handler and the webchat handler on STOP keywords."""
await optout_store.set_opted_out(phone_e164, source_channel, recorded_at=time.time())
# Mirror to Podium so their compliance view matches yours
await mark_contact_optout_in_podium(phone_e164)
async def handle_inbound_webchat(message: dict, auth: PodiumAuth) -> None:
phone = normalize_phone(message["from"], message.get("country") or "AU")
text = message["body"].strip().upper()
if text in OPTOUT_KEYWORDS:
await record_optout(phone, source_channel="webchat")
return # do NOT send any reply
if await check_optout(phone):
# Customer previously opted out via SMS; refuse to handle the webchat
# outbound side. Log for audit, do not reply.
log_optout_blocked(phone, channel="webchat")
return
await process_webchat_message(message, auth)The opt-out check must run on every outbound attempt — not just at session start — because the opt-out can land between the session opening and a reply being composed. Cache the opt-out lookup for at most 60 seconds; longer caching reintroduces the propagation lag.
| HTTP Status | Podium Error | Root Cause | Action |
|---|---|---|---|
400 Bad Request | invalid_phone_format | Phone not in E.164 | Normalize at the widget before submit |
400 Bad Request | invalid_location_uid | Unknown or wrong-format location_uid | Reload the locations list; validate before submit |
409 Conflict | contact_already_exists | Race lost on contact creation | Refetch by phone; return the winner |
413 Payload Too Large | attachment_exceeds_limit | Attachment > 25 MiB | Validate client-side before upload |
429 Too Many Requests | rate_limited | Burst exceeded Podium per-location cap | Honor Retry-After; see podium-rate-limit-survival |
451 Unavailable For Legal Reasons | contact_opted_out | Outbound to a STOP'd contact | Block at your handler before the call ever reaches Podium |
python3 scripts/phone_normalize.py --phone "0412 345 678" --default-country AU
# +61412345678
python3 scripts/phone_normalize.py --phone "(415) 555-1234" --default-country US
# +14155551234from fastapi import FastAPI, Request, HTTPException
from podium_auth import PodiumAuth
from webchat_ingest import process_inbound_webchat
app = FastAPI()
auth = PodiumAuth(...)
@app.post("/podium/webchat")
async def webchat_webhook(req: Request):
payload = await req.json()
try:
await process_inbound_webchat(payload, auth)
except WebchatError as e:
raise HTTPException(status_code=400, detail=str(e))
return {"status": "ok"}import asyncio
from webchat_ingest import sessions, scan_sessions
async def session_loop():
while True:
await scan_sessions(sessions)
await asyncio.sleep(60)
# In your app startup
asyncio.create_task(session_loop())python3 scripts/optout_audit.py --phone "+61412345678"Output:
{
"phone": "+61412345678",
"optout_store": {"opted_out": true, "source": "sms", "recorded_at": 1746000000},
"podium_contact": {"opted_out": true},
"consistent": true
}location_uid validation against a startup-loaded valid set (no default fallback)© jeremylongshore, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 10 other files (scripts, references) in skills/.curated/podium-webchat-handler of jeremylongshore/tons-of-skills-marketplace.
Open the folder on GitHubat commit cfae287
Podium Webchat Handler next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Podium Webchat Handler this skilljeremylongshore/tons-of-skills-marketplace | 2.8k | — | ~4.7k | Automated safety check: Pass | MIT | |
| Sub2API AdminWei-Shaw/sub2api | 44k | 1 repos | ~717 | Automated safety check: Pass | LGPL-3.0 | |
| Firecrawl Build Onboardingfirecrawl/firecrawl | 190k | 1 repos | ~1.4k | Automated safety check: Notes | ISC | |
| ToolJet Marketplace Plugin BuilderToolJet/ToolJet | 41k | — | ~2.1k | Automated safety check: Pass | AGPL-3.0 | |
| Firecrawl Search Integrationfirecrawl/firecrawl | 190k | 1 repos | ~1.1k | Automated safety check: Pass | ISC | |
| CoinGecko API Reference2025Emma/vibe-coding-cn | 23k | 1 repos | ~646 | Automated safety check: Pass | MIT |
Wei-Shaw/sub2api
Manages a Sub2API deployment from the command line: accounts, redeem and invitation codes, groups, proxies, imports, exports and raw admin API calls.
firecrawl/firecrawl
Gets Firecrawl working in a project: signs you in through the browser, saves FIRECRAWL_API_KEY to .env and picks the first SDK or REST path.
ToolJet/ToolJet
Turns an API description, such as an OpenAPI file or a Postman collection, into a connector plugin for ToolJet's marketplace and checks it with the repo's validator.
firecrawl/firecrawl
Guidance for adding Firecrawl's /search endpoint to product code and agent workflows when a feature starts from a query rather than a URL.
2025Emma/vibe-coding-cn
CoinGecko API documentation - cryptocurrency market data API, price feeds, market cap, volume, historical data. Use when integrating CoinGecko API, building…
diegosouzapw/OmniRoute
Manages AI provider connections, API keys, OAuth flows and connection tests through OmniRoute's REST API across its 327-provider catalog.
jeremylongshore/tons-of-skills-marketplace
Execute this skill enables AI assistant to conduct a security-focused code review using the security-agent plugin.
jeremylongshore/tons-of-skills-marketplace
Build this skill automates the adaptation of pre-trained machine learning models using transfer learning techniques.
jeremylongshore/tons-of-skills-marketplace
Execute proactive auto-loading: automatically detects and loads agents.md files.
jeremylongshore/tons-of-skills-marketplace
Aggregate and centralize performance metrics from applications, systems, databases, caches, and services.
jeremylongshore/tons-of-skills-marketplace
Execute this skill enables AI assistant to analyze capacity requirements and plan for future growth.
jeremylongshore/tons-of-skills-marketplace
Process use when you need to work with database indexing. An agent skill from jeremylongshore/tons-of-skills-marketplace.
Categories
Ingest Podium webchat messages in production and survive the webchat-side failures — invalid phone formats accepted at the widget, contact auto-creation races producing duplicate records, session…. Podium Webchat Handler is an agent skill from jeremylongshore/tons-of-skills-marketplace. Ingest Podium webchat messages in production and survive the webchat-side failures — invalid phone formats accepted at the widget, contact auto-creation races producing duplicate records, session timeouts mid-conversation, attachment size overflows, cross-location chat routing wrong, and opt-out propagation lag.
Podium Webchat Handler fits situations like: hardening a webchat → API integration; building a multi-location chat widget; debugging duplicate contacts; recovering from a cross-location routing incident.
Run `npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a claude-code`. Or copy the skill folder (skills/.curated/podium-webchat-handler in jeremylongshore/tons-of-skills-marketplace) into .claude/skills/podium-webchat-handler in your project. Claude Code loads it when a task matches its description.
Run `npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a codex`. Or copy the skill folder (skills/.curated/podium-webchat-handler in jeremylongshore/tons-of-skills-marketplace) into .agents/skills/podium-webchat-handler in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add jeremylongshore/tons-of-skills-marketplace --skill podium-webchat-handler -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/podium-webchat-handler, .gemini/skills/podium-webchat-handler, .github/skills/podium-webchat-handler and .opencode/skills/podium-webchat-handler in your project.
Going by SKILL.md and its folder, Podium Webchat Handler needs Python for the scripts in its folder and the command-line tools its instructions call (python3 and pip). Our summary lists: Python 3; Node.js. Its frontmatter pre-approves these tools: Read, Write, Edit, Bash(curl:*), Bash(jq:*), Bash(python3:*), Grep. Compatibility (from SKILL.md): Designed for Claude Code.
SKILL.md names 4 domains. In commands or code: api.podium.com; the agent is likely to contact it when it follows the instructions. As links in the text: docs.podium.com, github.com and pypi.org. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Podium Webchat Handler is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.7k tokens (SKILL.md is roughly 19k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 7k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Podium Webchat Handler: Sub2API Admin (Wei-Shaw/sub2api, 44k stars), Firecrawl Build Onboarding (firecrawl/firecrawl, 190k stars), ToolJet Marketplace Plugin Builder (ToolJet/ToolJet, 41k stars) and Firecrawl Search Integration (firecrawl/firecrawl, 190k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
jeremylongshore (a GitHub user) maintains it in jeremylongshore/tons-of-skills-marketplace, which has 2,827 GitHub stars. The repository holds 3,342 skills in this directory. The repository was last updated on October 10, 2026.
Source: jeremylongshore/tons-of-skills-marketplace on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.