{"id":"pinme-email","name":"pinme-email","summary":"PinMeプロジェクト(Worker TypeScript)がメール送信(send_email)を統合する必要がある場合にこのスキルを活用してください。","body":"# PinMe Worker Email API Integration\n\nGuides how to call PinMe platform's email sending API in a PinMe Worker (TypeScript).\n\n## Environment Variables\n\nThe following environment variables are automatically injected when the Worker is created — no manual configuration needed:\n\n```typescript\n// backend/src/worker.ts\nexport interface Env {\n  DB: D1Database;\n  API_KEY: string;      // Project API Key — used for send_email authentication\n  BASE_URL?: string;    // Optional override for PinMe API base URL, defaults to https://pinme.cloud\n}\n```\n\n> `API_KEY` is the sole credential for the Worker to call PinMe platform APIs. When `BASE_URL` is not set, it defaults to `https://pinme.cloud`.\n\n---\n\n## Send Email API\n\n**Endpoint:** `POST {BASE_URL}/api/v4/send_email`\n**Authentication:** `X-API-Key` header (using `env.API_KEY`)\n**Sender:** Automatically set to `{project_name}@pinme.cloud`\n\n### Request Format\n\n```json\n{\n  \"to\": \"user@example.com\",\n  \"subject\": \"Your verification code\",\n  \"html\": \"<p>Your code is <strong>123456</strong></p>\"\n}\n```\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `to` | string | Yes | Recipient email address |\n| `subject` | string | Yes | Email subject |\n| `html` | string | Yes | HTML body |\n\n### Response Format\n\n**Success (200):**\n```json\n{ \"code\": 200, \"msg\": \"ok\", \"data\": { \"ok\": true } }\n```\n\n**Errors:**\n\n| HTTP Status | Meaning | data.error Example |\n|-------------|---------|-------------------|\n| 401 | API Key missing or invalid | `\"X-API-Key header is required\"` / `\"Invalid API key\"` |\n| 400 | Parameter validation failed | `\"Invalid email address\"` / `\"Subject is required\"` |\n| 500 | Email service error | `\"Failed to send email\"` |\n\n### Worker Example Code\n\n```typescript\nasync function sendEmail(env: Env, to: string, subject: string, html: string): Promise<{ ok: boolean; error?: string }> {\n  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';\n  const resp = await fetch(`${baseUrl}/api/v4/send_email`, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'X-API-Key': env.API_KEY,\n    },\n    body: JSON.stringify({ to, subject, html }),\n  });\n\n  const result = await resp.json() as { code: number; msg: string; data?: { ok?: boolean; error?: string } };\n\n  if (resp.status !== 200 || result.code !== 200) {\n    return { ok: false, error: result.data?.error || result.msg || 'Unknown error' };\n  }\n  return { ok: true };\n}\n\n// Usage in routes\nasync function handleSendVerification(request: Request, env: Env): Promise<Response> {\n  const { email } = await request.json() as { email: string };\n  const code = Math.random().toString().slice(2, 8);\n\n  const result = await sendEmail(env, email, 'Verification Code',\n    `<p>Your code is <strong>${code}</strong></p>`);\n\n  if (!result.ok) {\n    return json({ error: result.error }, 500);\n  }\n  return json({ ok: true });\n}\n```\n\n---\n\n## Error Handling Pattern\n\nPinMe platform API unified response format:\n\n```typescript\ninterface PinmeResponse<T = unknown> {\n  code: number;   // 200=success, other=failure\n  msg: string;    // \"ok\" | \"error\" | \"invalid params\"\n  data?: T;       // Business data on success, may contain { error: string } on failure\n}\n```\n\n### Recommended Unified Error Handler\n\n```typescript\nasync function callPinmeAPI<T>(url: string, apiKey: string, body: unknown): Promise<{ data?: T; error?: string }> {\n  let resp: Response;\n  try {\n    resp = await fetch(url, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey },\n      body: JSON.stringify(body),\n    });\n  } catch {\n    return { error: 'Network error' };\n  }\n\n  if (!resp.ok) {\n    try {\n      const err = await resp.json() as PinmeResponse;\n      return { error: err.data && typeof err.data === 'object' && 'error' in err.data\n        ? (err.data as { error: string }).error\n        : err.msg || `HTTP ${resp.status}` };\n    } catch {\n      return { error: `HTTP ${resp.status}` };\n    }\n  }\n\n  const result = await resp.json() as PinmeResponse<T>;\n  if (result.code !== 200) {\n    return { error: result.data && typeof result.data === 'object' && 'error' in result.data\n      ? (result.data as { error: string }).error\n      : result.msg };\n  }\n  return { data: result.data as T };\n}\n```\n\n### Usage Example\n\n```typescript\nconst baseUrl = env.BASE_URL ?? 'https://pinme.cloud';\n\n// Send email\nconst emailResult = await callPinmeAPI<{ ok: boolean }>(\n  `${baseUrl}/api/v4/send_email`, env.API_KEY,\n  { to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>' },\n);\nif (emailResult.error) return json({ error: emailResult.error }, 500);\n```","author":"@glitternetwork","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/glitternetwork/pinme/tree/main/skills/pinme-email","license":"MIT","category":"coding","lang":"en","tokens":1213,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["pinme.cloud"]}}