Tutorial: Launch a Standalone Product¶
This tutorial walks through launching a standalone product on Kiket's API — from zero to first user signup. We'll use Správce GDPR (a GDPR request management tool) as the example.
Prerequisites¶
- Access to Kiket admin panel (super admin)
- A GitHub repository for your product definitions
- A Stripe account (for billing)
- A frontend app (Next.js, React, etc.)
Step 1: Register Your Product¶
In the Kiket admin panel, go to Platform > Products > New Product.
| Field | Example Value |
|---|---|
| Name | Správce GDPR |
| Slug | spravce-gdpr |
| Domain | spravce-gdpr.cz |
| Default Locale | cs (Czech) |
| Default Currency | CZK |
| Status | Active |
CORS Allowed Origins:
Branding (JSON):
{
"product_name": "Správce GDPR",
"from_email": "noreply@spravce-gdpr.cz",
"support_email": "podpora@spravce-gdpr.cz",
"logo_url": "https://spravce-gdpr.cz/logo.svg",
"primary_color": "#1a73e8"
}
Feature Flags (JSON):
{
"api_scopes": ["issues", "workflows", "intake_forms", "compliance_reports", "blockchain", "comments", "documents"],
"max_users_per_org": 50,
"ai_enabled": true,
"document_workflows": true
}
Step 2: Create Your Definitions Repository¶
Create a GitHub repo (e.g., github.com/your-org/spravce-gdpr-definitions) with this structure:
.kiket/
├── project.yaml
├── issue_types.yaml
├── workflows/
│ └── gdpr-dsar.yaml
├── intakes/
│ └── gdpr-request.yaml
└── boards/
└── dsar-board.yaml
See YAML Definition Reference for complete examples of each file.
Link the repository¶
- In the Kiket admin, go to Workflow Repositories > New
- Enter your GitHub repo URL, branch (
main), and path (.kiket/workflows) - Save — Kiket will sync definitions automatically
- Edit your Product Application and set the Workflow Repository to the one you just created
When new customers register, their organization automatically gets a copy of your definitions via the sync infrastructure.
Step 3: Set Up Stripe Connect¶
- Create a Stripe Connect Standard account for your product
- In the Kiket admin, edit your Product Application:
- Set Stripe Account ID (
acct_...) - Set Stripe Publishable Key (
pk_...) - Create products and prices in your Stripe Dashboard:
- Solo: 490 CZK/month
- Team: 990 CZK/month
- Agency: 3,490 CZK/month
- Add price IDs to your Feature Flags:
{
"api_scopes": ["issues", "workflows", "intake_forms", "compliance_reports", "blockchain"],
"pricing": {
"solo": { "stripe_price_id": "price_..." },
"team": { "stripe_price_id": "price_..." },
"agency": { "stripe_price_id": "price_..." }
}
}
- Configure Stripe webhook endpoint:
- URL:
https://kiket.dev/api/v1/billing/stripe_webhook - Events:
customer.subscription.*,invoice.paid,invoice.payment_failed - Set
STRIPE_CONNECT_WEBHOOK_SECRETenvironment variable
Step 4: Build Your Frontend¶
Create a Next.js (or similar) app at spravce-gdpr.cz.
Auth integration¶
// lib/api.ts
const API_BASE = 'https://kiket.dev/api/v1';
const PRODUCT_SLUG = 'spravce-gdpr';
async function register(data: {
email: string;
password: string;
password_confirmation: string;
name: string;
organization_name: string;
}) {
const res = await fetch(`${API_BASE}/auth/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Product-Slug': PRODUCT_SLUG,
},
body: JSON.stringify(data),
});
return res.json();
// Returns: { access_token, refresh_token, user, organization, subscription }
}
async function login(email: string, password: string) {
const res = await fetch(`${API_BASE}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Product-Slug': PRODUCT_SLUG,
},
body: JSON.stringify({ email, password }),
});
const data = await res.json();
// Handle multi-org case
if (data.select_organization) {
// Show org picker UI, then call /auth/login/select_org
return { selectOrg: true, organizations: data.organizations };
}
return data; // { access_token, refresh_token, user, organization }
}
async function apiCall(path: string, token: string, options?: RequestInit) {
const res = await fetch(`${API_BASE}${path}`, {
...options,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'X-Product-Slug': PRODUCT_SLUG,
'Accept-Language': 'cs',
...options?.headers,
},
});
return res.json();
}
Token refresh¶
async function refreshTokens(refreshToken: string) {
const res = await fetch(`${API_BASE}/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refreshToken }),
});
if (res.status === 403) {
// Subscription expired — redirect to billing
window.location.href = '/billing';
return null;
}
return res.json(); // { access_token, refresh_token, expires_in }
}
Listing DSAR requests¶
// Fetch issues (DSARs) from the user's project
const dsars = await apiCall('/issues?issue_type=dsar&status=received,verification,processing', token);
// Transition a DSAR to the next state
await apiCall(`/issues/${issueId}/transition`, token, {
method: 'POST',
body: JSON.stringify({ transition: { state: 'verification' } }),
});
Billing integration¶
async function startCheckout(planTier: string) {
const data = await apiCall('/billing/checkout_session', token, {
method: 'POST',
body: JSON.stringify({
plan_tier: planTier,
success_url: `${window.location.origin}/billing/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${window.location.origin}/billing`,
}),
});
// Redirect to Stripe Checkout
window.location.href = data.checkout_url;
}
Step 5: Deploy and Test¶
- Deploy your frontend to your domain
- Register a test account through your product's registration form
- Verify:
- Registration creates a user + organization + trial subscription
- Login returns a JWT scoped to your product
- Issues API returns data from your organization only
- Intake form is accessible at your configured URL
- Email subjects show your product name (not "Kiket")
- Billing checkout redirects to Stripe with your product branding
Step 6: Go Live Checklist¶
- Product registered in Kiket admin with correct branding
- Definitions repo synced (workflows, forms, issue types, boards)
- Stripe Connect configured with prices for all plan tiers
- CORS origins set for production domain
- DNS configured for your product domain
- Email sending configured (SPF/DKIM for your from_email domain)
- Intake form tested (public submission → issue creation)
- Workflow transitions tested (full lifecycle)
- Billing flow tested (registration → trial → checkout → active)
- Password reset flow tested (white-labeled email)
- Error messages display in correct locale
Next Steps¶
- Intake Forms API — public form submission and management
- Compliance Reports — generate SOX/HIPAA/GDPR reports
- Webhook Subscriptions — real-time event notifications
- YAML Definitions — workflow, form, and field reference