podium-webchat-handler
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", "podium chat widget", "podium phone validation", "podium contact dedup", "podium webchat session", "podium opt-out", "podium location routing".
What this skill does
# Podium Webchat Handler
## Overview
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:
1. **Invalid phone formats accepted** — Webchat asks for a phone number and a customer types `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.
2. **Contact auto-creation race produces duplicates** — Two webchats arrive within milliseconds from the same phone (customer opens two tabs, or a webhook retry overlaps the first delivery). Both handlers check "does a contact with this phone exist?", both see no, both create — and now the same human is two contact records, with conversation history split across both.
3. **Webchat sessions time out mid-conversation** — Podium webchat sessions have a server-side expiry (default ~30 min idle). A customer types `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.
4. **Attachment size overflows** — Podium accepts attachments up to 25MB. Webchat-to-API integrations that don't validate size client-side before upload fail server-side with a 413 — but only after the customer has waited through the upload progress bar. The customer thinks the image was sent; the agent never sees it.
5. **Cross-location chat routing is wrong** — A Sydney-based store and a Burleigh Heads–based store share the same Podium org. The webchat widget is embedded on a single corporate site and doesn't pass `location_uid` on the initial message. Every chat lands in the default location's queue regardless of which store the customer was actually browsing.
6. **Opt-out propagation lag** — A customer types STOP in an SMS thread. The opt-out flag is recorded in the SMS subsystem but not propagated to the webchat subsystem. Five minutes later the customer starts a new webchat session; the integration still tries to send an SMS confirmation reply and trips a compliance violation.
## Prerequisites
- Python 3.10+ (examples) or Node.js 18+
- `podium-auth` skill installed and a working `PodiumAuth` instance for OAuth token management
- `podium-webhook-reliability` skill installed if consuming webchat events via webhook (HMAC + dedup live there)
- `phonenumbers` library (Google's libphonenumber port): `pip install phonenumbers`
- Podium org with at least one location configured; for multi-location, the full `location_uid` list
- A contact store with a unique index on the normalized E.164 phone column (the natural dedup key)
## Instructions
Build in this order. Each section neutralizes one production failure mode.
### 1. E.164 phone normalization at the widget edge (neutralizes invalid phone formats)
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:
```python
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.
### 2. Contact auto-creation race (neutralizes duplicate contact records)
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:
```python
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):
pass
```
In 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.
### 3. Webchat session timeout monitor (neutralizes mid-conversation context loss)
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:
```python
import time
from dataclasses import dataclass, fielRelated in Backend & APIs
jfrog
IncludedInteract with the JFrog Platform via the JFrog CLI and REST/GraphQL APIs. Use this skill when the user wants to manage Artifactory repositories, upload or download artifacts, manage builds, configure permissions, manage users and groups, work with access tokens, configure JFrog CLI servers, search artifacts, manage properties, set up replication, manage JFrog Projects, run security audits or scans, look up CVE details, query exposures scan results from JFrog Advanced Security, manage release bundles and lifecycle operations, aggregate or export platform data, or perform any JFrog Platform administration task. Also use when the user mentions jf, jfrog, artifactory, xray, distribution, evidence, apptrust, onemodel, graphql, workers, mission control, curation, advanced security, exposures, or any JFrog product name.
cupynumeric-migration-readiness
IncludedPre-migration readiness assessor for porting NumPy to cuPyNumeric. Use BEFORE substantial porting work begins when the user asks whether code will scale on GPU, whether they should migrate to cuPyNumeric, which NumPy patterns transfer cleanly, what must be refactored before porting, or mentions pre-port assessment, scaling analysis, or refactor planning. Inspect the user's source code, look up NumPy usage, cross-reference the cuPyNumeric API support manifest, and distinguish distributed-scaling-friendly patterns from blockers such as unsupported APIs, scalar synchronization, host round-trips, Python/object-heavy control flow, shape/data-dependent branching, and in-place mutation hazards. Produce a verdict of READY, LIGHT REFACTOR, SIGNIFICANT REFACTOR, or NOT RECOMMENDED, with concrete refactor pointers.
alibabacloud-data-agent-skill
IncludedInvoke Alibaba Cloud Apsara Data Agent for Analytics via CLI to perform natural language-driven data analysis on enterprise databases. Data Agent for Analytics is an intelligent data analysis agent developed by Alibaba Cloud Database team for enterprise users. It automatically completes requirement analysis, data understanding, analysis insights, and report generation based on natural language descriptions. This tool supports: discovering data resources (instances/databases/tables) managed in DMS, initiating query or deep analysis sessions, real-time progress tracking, and retrieving analysis conclusions and generated reports. Use this Skill when users need to query databases, analyze data trends, generate data reports, ask questions in natural language, or mention "Data Agent", "data analysis", "database query", "SQL analysis", "data insights".
token-optimizer
IncludedReduce OpenClaw token usage and API costs through smart model routing, heartbeat optimization, budget tracking, and native 2026.2.15 features (session pruning, bootstrap size limits, cache TTL alignment). Use when token costs are high, API rate limits are being hit, or hosting multiple agents at scale. The 4 executable scripts (context_optimizer, model_router, heartbeat_optimizer, token_tracker) are local-only — no network requests, no subprocess calls, no system modifications. Reference files (PROVIDERS.md, config-patches.json) document optional multi-provider strategies that require external API keys and network access if you choose to use them. See SECURITY.md for full breakdown.
resend-cli
IncludedUse this skill when the task is specifically about operating Resend from an AI agent, terminal session, or CI job via the official resend CLI: installing/authenticating the CLI, sending/listing/updating/cancelling emails, batch sends, domains and DNS, webhooks and local listeners, inbound receiving, contacts, topics, segments, broadcasts, templates, API keys, profiles, or debugging Resend CLI/API failures. Trigger on mentions of Resend CLI, `resend`, `resend doctor`, `resend emails send`, `resend domains`, `resend webhooks listen`, `resend emails receiving`, or agent-friendly terminal automation.
alibabacloud-odps-maxframe-coding
IncludedUse this skill for MaxFrame SDK development and documentation navigation on Alibaba Cloud MaxCompute (ODPS). Helps answer MaxFrame API, concept, official example, and supported pandas API questions; create data processing programs; read/write MaxCompute tables; debug jobs (remote or local); and build custom DPE runtime images. Trigger when users mention MaxFrame, MaxCompute with MaxFrame, ODPS table processing, DPE runtime, MaxFrame docs/examples, DataFrame/Tensor operations, or GPU runtime setup. Works for both English and Chinese queries about Alibaba Cloud data processing with MaxFrame.