All API routes live under /api/. They return a consistent JSON response format:
Success:
{
"success": true,
"payload": { ... },
"message": "Optional message"
}Error:
{
"success": false,
"error": "Error description",
"code": "OPTIONAL_ERROR_CODE"
}Authentication
Send Magic Link
POST /api/auth/magic-link/sendSends a magic link email to the user. Works for both new and existing users.
Body:
{
"email": "user@example.com",
"callbackUrl": "/dashboard" // optional, where to redirect after login
}Access: Public
Verify Invite Token
GET /api/auth/verify-invite?token=abc123Checks if an invite token is valid (not expired, not used, not revoked).
Response:
{
"success": true,
"payload": {
"email": "invited@example.com",
"expiresAt": "2025-01-20T00:00:00Z"
}
}Access: Public
Sign Up With Invite
POST /api/auth/signup-with-inviteCreates a user account using a valid invite token.
Body:
{
"token": "invite-token-here"
}Access: Public
User Profile
Get Profile
GET /api/profileReturns the currently authenticated user's profile.
Response:
{
"success": true,
"payload": {
"id": "user-id",
"name": "John Doe",
"email": "john@example.com",
"image": "https://...",
"roles": ["USER"],
"isActive": true,
"planId": "product-id-or-null",
"createdAt": "2025-01-01T00:00:00Z"
}
}Access: Authenticated
Update Profile
PUT /api/profileUpdates the current user's profile information.
Body:
{
"name": "New Name"
}Access: Authenticated
Payments
Create Checkout Session
POST /api/payment/checkoutCreates a checkout session with the configured payment provider. Redirects the user to the provider's hosted checkout page.
Body:
{
"productId": "your-product-id",
"billingPeriod": "monthly" // "monthly" or "yearly" (subscription model only)
}Response:
{
"success": true,
"payload": {
"sessionId": "session-id",
"checkoutUrl": "https://checkout.provider.com/...",
"provider": "STRIPE"
}
}Access: Authenticated
Notes:
- For one-time payment model,
billingPeriodis ignored - Returns 409 if user already has an active subscription (subscription model)
Get Payment History
GET /api/payment/historyReturns all payments for the current user.
Response:
{
"success": true,
"payload": [
{
"id": "payment-id",
"provider": "STRIPE",
"status": "COMPLETED",
"amount": 149,
"currency": "USD",
"planId": "product-id",
"createdAt": "2025-01-15T00:00:00Z"
}
]
}Access: Authenticated
Payment Webhooks
POST /api/payment/webhook/stripe
POST /api/payment/webhook/dodo
POST /api/payment/webhook/polarReceives webhook events from the payment provider. Only one of these exists in your variant.
Access: Public (verified by webhook signature)
Notes:
- Do not call these manually. They are called by your payment provider.
- The webhook secret in your env vars must match the one configured in the provider dashboard.
Subscriptions
Get Active Subscription
GET /api/subscriptionReturns the current user's active subscription, or null if they do not have one.
Response:
{
"success": true,
"payload": {
"id": "sub-id",
"provider": "STRIPE",
"status": "ACTIVE",
"planId": "product-id",
"currentPeriodStart": "2025-01-01T00:00:00Z",
"currentPeriodEnd": "2025-02-01T00:00:00Z",
"cancelAt": null
}
}Access: Authenticated
Get Billing Portal URL
GET /api/subscription/billing-portal?returnUrl=/paymentsReturns a URL to the payment provider's customer portal where users can manage their subscription.
Query params:
returnUrl- Where to redirect after the user is done (required)
Response:
{
"success": true,
"payload": {
"url": "https://billing.provider.com/portal/..."
}
}Access: Authenticated (must have an active subscription)
Waiting List
Join Waitlist
POST /api/waiting-listAdds an email to the waitlist. Only works when platform mode is WAITLIST_WITH_INVITES.
Body:
{
"email": "user@example.com"
}Access: Public
Notes:
- Returns 409 if email is already on the waitlist
- Sends a welcome email automatically
Send Emails to Waitlist (Admin)
POST /api/waiting-list/send-emailsSends launch or reminder emails to selected waitlist entries.
Body:
{
"emails": ["user1@example.com", "user2@example.com"],
"type": "launch" // "launch" or "reminder"
}Response:
{
"success": true,
"payload": {
"sent": 5,
"failed": 1
}
}Access: Admin only
Contact
Submit Contact Form
POST /api/contactSends a contact form notification email to the admin.
Body:
{
"name": "John Doe",
"email": "john@example.com",
"message": "I have a question about..."
}Access: Public
Admin Endpoints
List All Users
GET /api/usersReturns all registered users.
Access: Admin only
Toggle User Status
POST /api/users/:userId/toggle-statusEnables or disables a user account. Cannot disable admin users.
Access: Admin only
Start Impersonation
POST /api/admin/impersonateStart impersonating a user. Sets a signed cookie.
Body:
{
"targetUserId": "user-id-to-impersonate"
}Access: Admin only
Get Impersonation Status
GET /api/admin/impersonateCheck if currently impersonating someone.
Access: Authenticated
Stop Impersonation
DELETE /api/admin/impersonateStop impersonating and return to admin view.
Access: Authenticated
List Waitlist Entries (Admin)
GET /api/admin/waiting-listReturns all waitlist entries with their status.
Access: Admin only
List Invites
GET /api/admin/invitesReturns all invite records.
Access: Admin only
Create Invite
POST /api/admin/invitesCreates a new invite and sends the invitation email.
Body:
{
"email": "newuser@example.com",
"expiresInDays": 7
}Access: Admin only
Notes:
- Returns 409 if user already exists or invite already active
Delete Invite
DELETE /api/admin/invites/:emailPermanently deletes an invite record.
Access: Admin only
Revoke Invite
POST /api/admin/invites/:email/revokeMarks an invite as revoked (keeps the record but makes it unusable).
Access: Admin only
Error Codes
Common error responses you may encounter:
| Status | Meaning |
|---|---|
| 400 | Bad request (validation failed or invalid input) |
| 401 | Not authenticated (no valid session) |
| 403 | Forbidden (insufficient role or account disabled) |
| 404 | Resource not found |
| 409 | Conflict (duplicate resource) |
| 500 | Internal server error |
Some errors include a code field for programmatic handling:
{
"error": "You already have an active subscription",
"code": "ACTIVE_SUBSCRIPTION_EXISTS"
}Making API Calls from the Frontend
MoveFast uses an axios client pre-configured with the base URL. Import it and use the API_ROUTES constants:
import { axiosClient } from "@/lib/axios";
import { API_ROUTES } from "@/lib/api-routes";
// Example: fetch payment history
const response = await axiosClient.get(API_ROUTES.PAYMENT.HISTORY);
const payments = response.data.payload;
// Example: create checkout
const response = await axiosClient.post(API_ROUTES.PAYMENT.CHECKOUT, {
productId: "your-product-id",
});
const { checkoutUrl } = response.data.payload;
window.location.href = checkoutUrl;The axios client automatically includes cookies for authentication and handles the /api prefix.
