Skip to main content

Developer Portal Service — Jira-Ready Epics & User Stories

Status: Draft Owner: Product + Developer Relations (DevRel) Last Updated: 2026-04-20 Service prefix: DEV Scope: Public developer portal, official server SDKs (Node, Python, Java, .NET, Go, PHP), mobile SDKs (Android, iOS, Flutter), Postman/OpenAPI/snippet generator, and the managed Verify API (SMS/Voice/WhatsApp).


Epic Summary

Epic IDTitleStoriesPoints
EP-DEV-01Public Developer Portal (docs, sandbox, key self-serve, consumption analytics)US-DEV-001 – US-DEV-00847
EP-DEV-02Official Server SDKs (Node, Python, Java, .NET, Go, PHP)US-DEV-009 – US-DEV-01448
EP-DEV-03Mobile SDKs (Android, iOS, Flutter)US-DEV-015 – US-DEV-01934
EP-DEV-04Postman Collection, OpenAPI Pre-Baked, Code Snippet GeneratorUS-DEV-020 – US-DEV-02324
EP-DEV-05Verify API (managed OTP across SMS/Voice/WhatsApp)US-DEV-024 – US-DEV-02945

Total stories: 29


EP-DEV-01 · Public Developer Portal

Context: The portal is the front door for every developer building on the Ghasi backbone — docs, sandbox, self-serve API keys, consumption analytics, and trust signals (status, incidents).

US-DEV-001 · Public Documentation Site Renders from OpenAPI

Type: Feature | Points: 5

Description: As an external developer, I want to browse versioned API reference documentation rendered from the canonical OpenAPI 3.1 spec so that I can understand every endpoint without reading source code.

Acceptance Criteria:

  • Canonical openapi/v1.json artifact rendered in Redoc at /reference/v1
  • Sidebar navigation lists every tag; deep-linking via #/operations/{operationId} anchors
  • Swagger UI at /reference/v1/try for interactive sandbox-only "Try it" calls
  • CI rejects publishes that fail spectral lint
  • Multi-version routes /reference/v{N.M} generated per release

US-DEV-002 · Locale Switching for Documentation (English / Pashto / Dari)

Type: Feature | Points: 5

Description: As an Afghan developer reading docs in their preferred language, I want to switch the portal between English, Pashto, and Dari so that I can learn the platform in my native language.

Acceptance Criteria:

  • Language switcher persists choice in localStorage and pref-lang cookie
  • Pashto/Dari trigger RTL layout (snapshot tested)
  • Untranslated guides show banner + render English fallback
  • First-visit language honours Accept-Language
  • Reference uses x-i18n.{locale} extension when present

US-DEV-003 · Self-Serve Sandbox API Key Creation

Type: Feature | Points: 5

Description: As a logged-in developer, I want to generate a sandbox API key from the portal in one click so that I can start integrating without filing a support ticket.

Acceptance Criteria:

  • POST /devportal/v1/keys modal collects label + scope checkboxes
  • Calls auth-service CreateApiKey(env=sandbox) via gRPC; returns 201 with { keyId, prefix, secret, env, scopes, createdAt }
  • Secret shown exactly once with copy-to-clipboard + confirmation; never persisted in devportal Postgres
  • 6th sandbox key returns 409 KEY_QUOTA_EXCEEDED
  • devportal.key.created NATS event emitted within 5 s

US-DEV-004 · Self-Serve Production API Key with Owner Approval

Type: Feature | Points: 8

Description: As a tenant developer needing production credentials, I want to request a production API key that the tenant owner approves in-portal so that production credentials are issued under owner control without a manual ticket.

Acceptance Criteria:

  • Production key requests trigger pending row + owner notification (in-portal + email) with one-click approve/deny
  • On approve, key is created via auth-service and shown to requester once on next portal load
  • 7-day expiry on unanswered requests (status=EXPIRED + notify)
  • Owner deny returns status: DENIED with reason
  • Self-owners get auto-approval (synchronous secret)

US-DEV-005 · API Key Revocation Propagates Within 60 Seconds

Type: Feature | Points: 5

Description: As a developer who suspects a leaked key, I want to revoke a key in one click and have it stop working everywhere within 60 s so that compromise blast-radius is bounded.

Acceptance Criteria:

  • DELETE /devportal/v1/keys/{keyId} invokes auth-service RevokeApiKey and emits devportal.key.revoked
  • Synthetic prober confirms P95 ≤ 60 s, P99 ≤ 120 s propagation
  • Revoked-key requests return 401 API_KEY_REVOKED
  • Double-revoke returns 200 alreadyRevoked: true, no duplicate event
  • Revoked keys cannot be reactivated (must create a new key)

US-DEV-006 · Sandbox Endpoint Returns Deterministic Synthetic DLR

Type: Feature | Points: 8

Description: As a developer integrating against sandbox, I want to call POST https://api-sandbox.ghasi.af/v1/sms/send and receive a deterministic DLR so that I can write reliable integration tests.

Acceptance Criteria:

  • Sandbox returns same response shape as production
  • +93700000001 → DELIVERED in 2 s
  • +93700000002 → UNDELIV ABSENT_SUBSCRIBER
  • +93700000003 → no DLR for 30 m, then EXPIRED
  • Other recipients → DELIVERED with random 200–1500 ms latency
  • Production key against sandbox returns 403 ENV_MISMATCH

US-DEV-007 · Per-Key Consumption Analytics Dashboard

Type: Feature | Points: 8

Description: As a developer monitoring my integration, I want to see per-key request volume, error rate, P95 latency, and SMS segment count over a chosen time range so that I can detect anomalies and plan capacity.

Acceptance Criteria:

  • /dashboard/keys/{keyId} renders 4 charts for ranges 1h / 24h / 7d / 30d
  • gRPC QueryConsumption to analytics-service; Redis cache 30 s
  • CSV export ≤ 10 000 rows with [ts, requests, errors, p50, p95, p99, segments]
  • Analytics outage shows empty state + retry; no toast spam
  • Range > 90 days → 400 RANGE_TOO_LARGE

US-DEV-008 · Status Page Embed and Incident Banner

Type: Feature | Points: 3

Description: As a developer worried about an outage, I want to see real-time platform status on every portal page so that I know whether an issue is mine or the platform's.

Acceptance Criteria:

  • Status indicator polls status.ghasi.af/api/v2/status.json every 60 s
  • Active incident ≥ minor renders banner with title + link
  • Status feed unreachable → no banner, log warn, no broken UI
  • Per-incident dismissal cookie respected

EP-DEV-02 · Official Server SDKs

Context: First-class server SDKs for Node, Python, Java, .NET, Go, and PHP — generated from OpenAPI, post-processed for idiom, gated by DevRel manual review per release.

US-DEV-009 · Node.js SDK Published to npm

Type: Feature | Points: 8

Description: As a Node.js developer, I want @ghasi/sdk from npm with client.sms.send(...) so that I do not write HTTP plumbing.

Acceptance Criteria:

  • Published @ghasi/sdk@1.M.P with TypeScript types, ESM + CJS dual build, changelog
  • client.sms.send({to, from, body}) returns Promise<{messageId, status: "QUEUED"}>
  • Transient 5xx → 3 retries with exponential backoff (200/400/800 ms)
  • 4xx → typed GhasiApiError(code, httpStatus, requestId)
  • Strict TS consumer compiles with tsc --noEmit
  • DevRel manual approval gate before npm publish

US-DEV-010 · Python SDK Published to PyPI

Type: Feature | Points: 8

Description: As a Python developer, I want pip install ghasi-sdk and client.sms.send(...) so that I have an idiomatic Pythonic client.

Acceptance Criteria:

  • Wheels for 3.10/3.11/3.12 + sdist published
  • Sync GhasiClient and AsyncGhasiClient (httpx async) both exposed
  • Errors: AuthError, RateLimitError(retry_after)
  • mypy --strict passes against the SDK
  • PEP 484 type hints throughout

US-DEV-011 · Java SDK Published to Maven Central

Type: Feature | Points: 8

Description: As a Java developer, I want af.ghasi:ghasi-sdk on Maven Central with client.sms().send(...) so that I have a typed, supported client.

Acceptance Criteria:

  • Maven Central artifact + sources JAR + Javadoc JAR
  • JDK 17 thread-safe GhasiClient.builder().apiKey(...).build()
  • Spring Boot starter ghasi-sdk-spring-boot-starter registers GhasiClient bean
  • Errors: GhasiApiException with code/status/requestId
  • Sonatype OSSRH validation passes

US-DEV-012 · .NET SDK Published to NuGet

Type: Feature | Points: 8

Description: As a .NET developer, I want Ghasi.Sdk from NuGet and await client.Sms.SendAsync(...) so that I have a first-class .NET client.

Acceptance Criteria:

  • Ghasi.Sdk published targeting net8.0 with source-link
  • services.AddGhasiClient(o => o.ApiKey = "...") DI registration
  • CancellationToken cancels in-flight HTTP
  • Source-generated System.Text.Json serialisers (Blazor WASM-safe)
  • Typed GhasiApiException

US-DEV-013 · Go SDK Published as a Module

Type: Feature | Points: 8

Description: As a Go developer, I want af.ghasi/sdk-go and context-aware client.SMS.Send(ctx, ...) so that I have an idiomatic Go client.

Acceptance Criteria:

  • Tagged v1.M.P on GitHub, resolvable by go get
  • Every method takes context.Context first
  • *ghasi.APIError for 4xx with Code, Status, RequestID
  • go vet and staticcheck zero-finding
  • WithRetryPolicy configurable backoff

US-DEV-014 · PHP SDK Published to Packagist

Type: Feature | Points: 8

Description: As a PHP developer, I want composer require ghasi/sdk with Laravel and Symfony bindings so that common PHP frameworks integrate cleanly.

Acceptance Criteria:

  • PHP 8.2+, PSR-12 compliant, published to Packagist
  • Laravel: GhasiServiceProvider + Ghasi facade auto-discovered
  • Symfony: Flex recipe + bundle service registration
  • Ghasi\Exception\ApiException with code/status/requestId
  • PHPStan level 8 zero errors

EP-DEV-03 · Mobile SDKs

Context: Native mobile SDKs (Android, iOS, Flutter) optimised for the Verify API — auto-OTP-read, accessibility, locale, reduced-motion.

US-DEV-015 · Android SDK with SMS Retriever Auto-Read

Type: Feature | Points: 8

Description: As an Android developer integrating Verify, I want an Android SDK that auto-reads SMS OTP using SMS Retriever / SMS User Consent APIs so that users do not manually copy codes.

Acceptance Criteria:

  • af.ghasi:android-sdk:1.M.P Gradle dependency
  • GhasiVerify.start(activity, verificationId) uses SMS Retriever (no READ_SMS permission)
  • onCodeReceived(code) fires within 5 s
  • Falls back to SMS User Consent API when needed
  • minSdk 24, compileSdk 34; ships consumer-rules.pro

US-DEV-016 · iOS SDK with OTP AutoFill Heuristics

Type: Feature | Points: 8

Description: As an iOS developer integrating Verify, I want a native Swift SDK with one-tap OTP autofill leveraging iOS heuristics so that users get the iOS standard experience.

Acceptance Criteria:

  • SwiftPM via https://github.com/ghasi-af/ios-sdk
  • GhasiVerify.start(verificationId) returns Combine publisher of VerifyState
  • iOS QuickType OTP suggestion via From: Ghasi SMS heuristics
  • iOS 15+
  • GhasiOTPField() SwiftUI view with .oneTimeCode content type
  • GhasiError typed errors via publisher failure

US-DEV-017 · Flutter SDK Published to pub.dev

Type: Feature | Points: 8

Description: As a Flutter developer, I want flutter pub add ghasi_sdk with unified Verify across Android and iOS so that I do not maintain native bridges.

Acceptance Criteria:

  • GhasiClient(apiKey: ...) exposes sms, verify, keys
  • Verify uses platform channel to native flows on each OS
  • pub.dev quality score ≥ 130/140
  • Sandbox-only HTTP testing works without platform channels
  • Flutter 3.16+, Dart 3.2+

US-DEV-018 · Mobile SDKs Surface Consistent Verify State Machine

Type: Feature | Points: 5

Description: As a mobile developer, I want the same VerifyState enum across Android, iOS, and Flutter so that cross-platform behaviour is predictable.

Acceptance Criteria:

  • Shared seven values: PENDING, AUTO_FILLED, USER_ENTERED, APPROVED, FAILED, CANCELLED, EXPIRED
  • State diagram documented + linked from each SDK README
  • CI contract test asserts identical sequence per platform on a deterministic sandbox verificationId

US-DEV-019 · Mobile SDKs Honour Reduced-Motion and Locale

Type: Feature | Points: 5

Description: As a mobile developer building inclusive apps, I want provided UI components to respect OS reduced-motion and active locale so that my app inherits accessibility for free.

Acceptance Criteria:

  • Reduced-motion disables non-essential animation
  • Locale ps/fa/en switches strings; missing → English fallback
  • RTL layout for Pashto/Dari with correct caret behaviour
  • GhasiVerify.localize(map) overrides bundled catalogue

EP-DEV-04 · Postman, OpenAPI, Snippet Generator

US-DEV-020 · Pre-Baked Postman Collection Per Release

Type: Feature | Points: 5

Description: As a developer evaluating the API, I want to click "Run in Postman" so that I can poke at endpoints in 30 seconds.

Acceptance Criteria:

  • Postman v2.1 JSON generated per release at immutable URL /postman/v1.M.P/collection.json
  • Imports two environments (sandbox/production)
  • Authorization: Bearer {{apiKey}} collection variable
  • Example responses (200/4xx/5xx) per request
  • Old release URLs remain accessible

US-DEV-021 · OpenAPI 3.1 Bundle Downloadable and Validated

Type: Feature | Points: 3

Description: As a developer using their own toolchain, I want to download the canonical OpenAPI 3.1 JSON/YAML so that I can generate clients in unsupported languages.

Acceptance Criteria:

  • GET /openapi/v1.json and /openapi/v1.yaml with correct Content-Type
  • Spec passes spectral lint and openapi-cli validate
  • openapi-generator Java client builds in CI
  • Historical versions immutable per release

US-DEV-022 · Per-Endpoint Code Snippet Generator (10+ Languages)

Type: Feature | Points: 8

Description: As a developer reading the docs, I want copy-paste snippets in my chosen language so that I do not translate from cURL manually.

Acceptance Criteria:

  • Languages: cURL, Node, Python, Java, .NET, Go, PHP, Ruby, Kotlin, Swift
  • Snippets use official SDK when available; raw HTTP otherwise
  • "Copy" button on hover; clipboard copy works
  • Endpoint changes regenerate snippets and purge CDN
  • Placeholders bracketed (e.g. <YOUR_API_KEY>)

US-DEV-023 · SDK Release Pipeline with DevRel Manual Gate

Type: Feature | Points: 8

Description: As a DevRel engineer, I want one GitHub Actions pipeline to publish all six server SDKs from one tag with a manual gate so that releases are atomic and auditable.

Acceptance Criteria:

  • One tag v1.M.P triggers per-language build, test, RC artifacts
  • release-approval GitHub environment requires DevRel reviewer
  • On approval: parallel publish to npm, PyPI, Maven Central, NuGet, GitHub releases (Go), Packagist
  • Per-language failure: Slack alert + auto-create follow-up issue, mark partial release
  • devportal.sdk.released event per language with {language, version, artifactUri, sha256}

EP-DEV-05 · Verify API (Managed OTP)

Context: A Twilio-Verify-equivalent product that abstracts code generation, storage, dispatch, and channel fallback behind one contract. Delegates SMS to sms-orchestrator, voice to voice-otp-gateway, WhatsApp to channel-router-service -> WA BSP.

US-DEV-024 · Verify API: Start SMS Verification

Type: Feature | Points: 8

Description: As a tenant integrating Verify, I want to call POST /v1/verify/sms so that the platform sends an OTP without me implementing code generation, storage, and SMS dispatch.

Acceptance Criteria:

  • Request {to, locale, codeLength, ttlSeconds, templateId?} returns 202 {verificationId, status: "PENDING", channel: "sms", expiresAt}
  • Code is crypto.randomInt, length 4/6/8; stored as HMAC-SHA-256(code, server-pepper) in Redis with TTL
  • SMS dispatched via sms-orchestrator using approved Verify template
  • Invalid to → 400 INVALID_RECIPIENT
  • Per-recipient cap exceeded → 429 RECIPIENT_RATE_LIMIT
  • devportal.verify.requested event emitted (recipient hashed)

US-DEV-025 · Verify API: Check OTP

Type: Feature | Points: 5

Description: As a tenant validating a user-entered code, I want POST /v1/verify/check so that I do not store OTPs or implement constant-time comparison.

Acceptance Criteria:

  • Match + unexpired + attempts ≤ 5 → 200 APPROVED; session deleted
  • Mismatch → 200 PENDING with attemptsRemaining; attempts++
  • attempts > 5 → 200 FAILED MAX_ATTEMPTS; session deleted
  • Expired → 200 EXPIRED; emit devportal.verify.failed
  • crypto.timingSafeEqual constant-time compare
  • On approval, emit devportal.verify.approved

US-DEV-026 · Verify API: Voice Channel

Type: Feature | Points: 8

Description: As a tenant whose user cannot receive SMS, I want POST /v1/verify/voice so that the platform places an outbound call reading the OTP twice.

Acceptance Criteria:

  • 202 {verificationId, status: "PENDING", channel: "voice", expiresAt}
  • TTS in ps/fa/en reads each digit individually with 500 ms gap, repeated twice
  • voice-otp-gateway invocation; up to 2 retries with 30 s spacing
  • Pickup completion → DELIVERED
  • Voice unavailable → 503 VOICE_UNAVAILABLE; no session created

US-DEV-027 · Verify API: WhatsApp Channel

Type: Feature | Points: 8

Description: As a tenant whose users prefer WhatsApp, I want POST /v1/verify/whatsapp so that OTP is delivered via the WhatsApp Cloud API.

Acceptance Criteria:

  • 202 with channel: "whatsapp"
  • Sends Meta-approved template verify_otp_v1 via channel-router-service → WA BSP adapter
  • Not-on-WhatsApp → FAILED with reason NOT_ON_WHATSAPP within 30 s
  • Delivery webhook updates session; emits devportal.verify.delivered
  • Regulator-blocked region → 451 CHANNEL_BLOCKED_BY_REGULATOR

US-DEV-028 · Verify API: Channel Fallback Cascade

Type: Feature | Points: 8

Description: As a tenant who wants maximum delivery rate, I want channels: ["whatsapp","sms","voice"] cascade so that I do not orchestrate fallback myself.

Acceptance Criteria:

  • POST /v1/verify accepts channels[]; 202 returns currentChannel, channelOrder
  • Per-channel timeout (default 60 s) advances to next; max 3 attempts
  • GET /v1/verify/{verificationId} returns full attempt history
  • Approval on any successful channel ends session
  • All channels failed → FAILED ALL_CHANNELS_FAILED + emit devportal.verify.failed

US-DEV-029 · Verify API: Per-Tenant Anti-Abuse and Spend Cap

Type: Feature | Points: 8

Description: As a platform operator, I want per-tenant Verify start rate limits and a daily spend cap so that OTP-grinding and runaway usage are bounded.

Acceptance Criteria:

  • Per-tenant rate exceeded → 429 TENANT_RATE_LIMIT (default 100/min)
  • Daily spend cap exceeded → 402 DAILY_SPEND_CAP_EXCEEDED + devportal.verify.spend_cap_hit event (default $500/day)
  • fraud-intel-service flag → 403 FRAUD_BLOCK with reference
  • Per-recipient cap (5/hour, 20/day) → 429 RECIPIENT_RATE_LIMIT
  • All blocks logged with full context; counters exposed to Prometheus