Spaces:
Running
API Reference
Base URL (production): https://shaileshrv1432-meme-backend.hf.space
Local: http://localhost:8000
Interactive docs are served live by the running app:
- RapiDoc β
/docs - ReDoc β
/redoc
Conventions
- Auth: endpoints marked π require a header
Authorization: Bearer <access_token>. - Admin: endpoints marked π‘ require header
X-Admin-Secret: <ADMIN_SECRET>and return404whenAPP_ENV=production. - Body type: most write endpoints take
application/x-www-form-urlencoded(Form(...)). A few take JSON (Body(...)) β noted per endpoint. - Tokens:
access_token(short-lived,ACCESS_TTL_SECONDS) +refresh_token(long-lived,REFRESH_TTL_SECONDS). Refresh viaPOST /auth/refresh.
Auth β /auth
POST /auth/request-otp
Send an OTP to a phone number (creates the user if new).
- Form:
phone(E.164, e.g.+9172048...) - 200:
{ "message": "OTP sent" } - 502: SMS provider send failed (invalid key / no wallet balance / KYC incomplete). With
OTP_DEBUG=truethe 502 detail contains the raw provider error. - Note: if
FAST2SMS_API_KEYis unset, no SMS is sent β the OTP is printed to the server logs ([DEV SMS] to=β¦ otp=β¦) so login still works in dev.
GET /auth/sms-check π‘
Diagnose OTP/SMS delivery without a real login. Admin secret via header X-Admin-Secret or ?secret= (browser-friendly).
- Query:
to(optional 10-digit number β if given, performs a real test send) - 200:
{ fast2sms_key_present, key_prefix, mode, verdict, provider_response? | provider_error?, hint } verdict:no_provider(key unset) Β·configured_not_tested(noto) Β·okΒ·send_failed
POST /auth/verify-otp
Verify an OTP and receive tokens.
- Form:
phone,otp - 200:
{ access_token, refresh_token, user: {...} } - 400: no user / no OTP / expired / invalid
- Guest: if
GUEST_MODE_ENABLED=trueandphone==GUEST_PHONE_NUMBERwithotp==GUEST_OTP_CODE, returns a guest session (account_type: "guest").
POST /auth/set-pin
Set a login PIN for a phone user.
- Form:
phone,pin(4β6 digits) - 200:
{ "message": "PIN set" } - 422: PIN not 4β6 digits
POST /auth/login-pin
Login with phone + PIN.
- Form:
phone,pin - 200:
{ access_token, refresh_token } - 401: invalid PIN Β· 400: no user / PIN not set
POST /auth/refresh
Exchange a refresh token for a fresh access token.
- Form:
refresh_token - 200:
{ access_token }Β· 401: invalid/expired
POST /auth/google
Verify a Google ID token and issue app tokens.
- Form:
id_token(Google ID token from the app) - 200:
{ access_token, refresh_token, user: {...} } - 503:
GOOGLE_WEB_CLIENT_IDnot set on the server Β· 401: invalid token - Users are keyed internally by
google:<sub>.
GET /auth/me π
Current user.
- 200:
{ id, phone_e164, name, email, avatar_url, created_at, is_active }
PATCH /auth/update-profile π
Update profile fields (form-encoded).
- Form (all optional):
name,email,avatar_url - 200: updated user object
DELETE /auth/delete-account π
Soft-delete (deactivate) the current account.
- Form:
confirm(bool, must be true) - 200:
{ "message": "Account deactivated" }
POST /auth/debug-token (dev only)
Decode a JWT without verifying signature. 404 in production.
Memes β /
POST /upload π
Upload an image; stores it and returns initial GIF suggestions.
- Multipart:
image(file),mood(optional) - 200:
{ status, uuid, object_key, url, mood, ...suggestions }
POST /suggest π
Re-run suggestions for an already-uploaded image.
- Form:
image_object_key,mood(optional) - 200:
{ status, object_key, mood, ...suggestions }Β· 404: key not found
POST /compose π
Compose a meme from form fields (single overlay).
- Form:
image_object_key,gif_url|gif_object_key,x,y,width, β¦ (overlay placement)
POST /compose/layout?output_format=gif|png|mp4 π
Compose from a full JSON layout (floating images and text over a background). This is what the app's editor uses.
- JSON body:
image_dimensions={ "bgImage": { "source": "<object_key|url>", "width": 0, "height": 0, "applyCoverBlurAndContainImageFilter": false }, "floatingImages": [ { "file_url": "...", "width": 200, "relativeX": 0, "relativeY": 0, "rotation": 0, "z": 0 } ], "textOverlays": [ { "text": "LOL", "fontSize": 32, "color": "#FFFFFF", "strokeColor": "#000000", "width": 240, "height": 60, "relativeX": 0, "relativeY": 0, "rotation": 0, "z": 1 } ] } - Query/body:
output_format(defaultgif) - 200:
{ object_key, url, ... } - Layering: every overlay (image or text) may carry a
z(integer). All overlays are composited in ascendingz, so higherz= on top. Omittingzfalls back to input order (images first, then text) β fully backward compatible. - Rotation:
rotation(degrees, clockwise) rotates an overlay about its centre;relativeX/Ystay the un-rotated box top-left. Defaults to0. - Text: rendered server-side (Pillow) as bold, stroked meme text;
color/strokeColorare hex,widthenables word-wrap,width+heightdefine the box the text is centred in. - Tip: pass image overlay
widthonly β the backend preserves the source aspect ratio (avoids stretched GIFs).
GET /memes?limit&offset π
List the current user's generated memes (latest first).
- 200:
{ items: [{ id, object_key, content_type, created_at, presigned_url, permanent_url }], count, offset, limit }
GET /memes/presign?object_key=β¦ π
Get a 10-minute presigned URL for one owned meme.
GET /memes/{object_key} π
Stream the meme bytes through the API (permanent proxy URL, cached 1 year).
Media β /media
POST /media/url π
Presign any media object (JSON body).
- Body:
object_key,expires_seconds(default 600) - 200:
{ object_key, url, expires_seconds }
POST /media/permanent-url π
Get the permanent proxy URL for an owned media object.
- Body:
object_key - 200:
{ object_key, permanent_url, type: "proxy", expires: "never" }
Profile β /profile
GET /profile π
Current user's profile (from Postgres).
PUT /profile π
Update name/email/avatar (JSON body).
- Body (optional):
name,email,avatar_url - 200:
{ "message": "Profile updated" }
Subscriptions β /subscriptions
Requires
RAZORPAY_KEY_ID+RAZORPAY_KEY_SECRETon the server. Plans (paise/INR):weekly5000 / 7d,monthly14000 / 30d,yearly80000 / 365d.
POST /subscriptions/order π
Create a Razorpay order.
- Form:
plan(weekly|monthly|yearly) - 200:
{ order_id, amount, currency, key_id, plan } - 400: unknown plan Β· 503: payments not configured / keys rejected (auth) Β· 502: order creation failed
- With
PAYMENTS_DEBUG=true, the 502 detail contains the raw Razorpay error (for debugging only).
POST /subscriptions/verify π
Verify the payment signature, activate the plan, and record a payments row (best-effort).
- Form:
razorpay_order_id,razorpay_payment_id,razorpay_signature,plan - 200:
{ status: "active", plan, expires_at } - 400: verification failed
GET /subscriptions/status π
Current subscription (auto-expires past the end date).
- 200:
{ plan, status, active, expires_at, is_premium, usage: { daily_meme_limit, daily_memes_used, daily_memes_remaining, premium_only_formats } }
GET /subscriptions/receipt π
Most recent payment, shaped for the receipt UI.
- 200:
{ id, plan, plan_label, amount, amount_display, currency, status, method, payment_id, order_id, paid_at, expires_at } - 404: no payment on record (client falls back to plan-derived data)
GET /subscriptions/payments π
Full payment/transaction history, newest first.
- 200:
{ payments: [ <receipt object>, β¦ ] }(empty list if none / table absent)
GET /subscriptions/receipt/{payment_id}.pdf π
Download a specific payment's receipt as a PDF (generated with Pillow). Scoped to the owner.
- 200:
application/pdf(Content-Disposition attachment) - 404: receipt not found / not owned
GET /subscriptions/config-check π‘
Diagnose Razorpay config without a checkout. Header X-Admin-Secret.
- 200:
{ key_id_present, key_secret_present, key_mode, key_id_prefix, secret_length, had_whitespace_or_quotes, razorpay_lib_installed, verdict, probe_order_id?, probe_error?, hint } ?probe=falseskips the live βΉ1 test order.
Admin β /admin π‘ (404 in production)
| Method | Path | Purpose |
|---|---|---|
| POST | /admin/seed-demo-user |
Create a demo user (phone, pin, otp, issue_tokens) |
| POST | /admin/minio-diagnose |
Check object-storage config |
| POST | /admin/test-sms |
Test the SMS provider |
| GET | /admin/users?limit&offset&search |
List users |
| GET | /admin/users/{id} |
Get one user |
| PUT | /admin/users/{id} |
Update name/email/is_active |
| DELETE | /admin/users/{id} |
Soft-delete (deactivate) |
| GET | /admin/cleanup-guest-data |
Purge expired guest data |
Health & docs
| Method | Path | Purpose |
|---|---|---|
| GET | /health/deps |
Dependency/health check |
| GET | /docs |
RapiDoc UI |
| GET | /redoc |
ReDoc UI |