MeuMore

MeuMore Creator API

Run your whole creator business from the tools you already use: publish and schedule posts, message fans (including mass and locked messages), see subscribers and spend, track earnings, request payouts and get webhooks when things happen.

Base URL https://meumore.com/api/v1 · OpenAPI 3.1 (openapi.json)

Authentication

Create a key in Settings → API (creators only). Keys look like mm_live_<id>_<secret> and are shown once: we only store a hash. Send the key in the Authorization header of every request. Revoke a key there at any time; it stops working immediately (401).

curl https://meumore.com/api/v1/me \
  -H "Authorization: Bearer mm_live_0123456789abcdef_…"

Keep keys on your server. Never put them in a browser app or a public repository.

Scopes

Each key has one or more scopes. GET /me and GET /stats work with any key. A missing scope returns 403 insufficient_scope.

  • content · upload, schedule, edit and delete posts
  • messages · read and send chat messages, mass messages
  • fans · subscribers and fan details
  • earnings · earnings, transactions and payouts
  • webhooks · manage webhook endpoints

Rate limits & errors

  • 60 requests per minute per key (a token bucket, so short bursts are fine).
  • Mass messages: 10 per hour. Chat messages: 30 per minute. Uploads: 20 per hour.
  • Over a limit you get 429 rate_limited; wait and retry.

Errors are JSON with a stable code and a readable message:

HTTP/1.1 403 Forbidden
{"error":{"code":"insufficient_scope","message":"This API key is missing the 'earnings' scope."}}

Codes include invalid_api_key, revoked_api_key, not_a_creator, insufficient_scope, invalid_request, invalid_json, not_found, conflict, already_published, payment_required, unprocessable (e.g. content rejected by moderation), rate_limited and internal_error.

Pagination

Lists return {"data": [...], "next_cursor": "…"}. Pass ?cursor=<next_cursor> for the next page and ?limit= (1–100, default 25). next_cursor is null on the last page. Amounts are integers in minor units (paise, cents) with an ISO 4217 currency; times are ISO 8601 UTC.

Endpoints

In the examples, $MEUMORE_KEY holds your key.

get/mescope: any

Your profile, creator status, subscription price and counts

curl -s https://meumore.com/api/v1/me -H "Authorization: Bearer $MEUMORE_KEY"
get/statsscope: any

Subscribers now, new and churned, revenue per day by source, top posts by unlocks

curl -s https://meumore.com/api/v1/stats?from=2026-09-01T00:00:00Z -H "Authorization: Bearer $MEUMORE_KEY"
post/media/uploadsscope: content

Get a presigned upload URL

PUT the file bytes to upload.url with upload.headers, then finalise with POST /media. Images up to 10 MB, videos up to 100 MB.

curl -s https://meumore.com/api/v1/media/uploads -H "Authorization: Bearer $MEUMORE_KEY" -H 'Content-Type: application/json' -d '{"content_type":"image/jpeg","size":123456}'
curl -X PUT -H 'Content-Type: image/jpeg' --data-binary @photo.jpg "$UPLOAD_URL"
get/mediascope: content

List your media, newest first

curl -s https://meumore.com/api/v1/media?limit=10 -H "Authorization: Bearer $MEUMORE_KEY"
post/mediascope: content

Finalise an upload: caption, visibility, price, scheduling

Processing and moderation run now. Explicit content is rejected (422). A future publish_at schedules the post: it stays hidden everywhere until then. context "chat" makes a vault item for messages that never shows in your gallery.

curl -s https://meumore.com/api/v1/media -H "Authorization: Bearer $MEUMORE_KEY" -H 'Content-Type: application/json' -d '{"upload_id":"$UPLOAD_ID","content_type":"image/jpeg","caption":"New set","visibility":"subscribers","publish_at":"2026-10-01T18:00:00Z"}'
get/media/{id}scope: content

One media item, with a short-lived URL to the original

curl -s https://meumore.com/api/v1/media/$MEDIA_ID -H "Authorization: Bearer $MEUMORE_KEY"
patch/media/{id}scope: content

Edit caption, visibility, price or schedule

publish_at can only change before the post is published; null publishes now.

curl -s -X PATCH https://meumore.com/api/v1/media/$MEDIA_ID -H "Authorization: Bearer $MEUMORE_KEY" -H 'Content-Type: application/json' -d '{"caption":"Updated"}'
delete/media/{id}scope: content

Delete a media item (buyers lose access)

curl -s -X DELETE https://meumore.com/api/v1/media/$MEDIA_ID -H "Authorization: Bearer $MEUMORE_KEY"
get/conversationsscope: messages

Your conversations, most recent first

curl -s https://meumore.com/api/v1/conversations -H "Authorization: Bearer $MEUMORE_KEY"
get/conversations/{id}/messagesscope: messages

Messages in a conversation, newest first

curl -s https://meumore.com/api/v1/conversations/$CONVERSATION_ID/messages -H "Authorization: Bearer $MEUMORE_KEY"
post/conversations/{id}/messagesscope: messages

Send a text, media or locked message

Text only, a media_id (one of your media; upload chat-only media with context "chat"), or a locked message: media_id plus tier_id. The fan sees a blurred preview and pays their local price to unlock. You can message fans who subscribe to you, and your matches. 30 messages per minute; text up to 2000 characters.

curl -s https://meumore.com/api/v1/conversations/$CONVERSATION_ID/messages -H "Authorization: Bearer $MEUMORE_KEY" -H 'Content-Type: application/json' -d '{"text":"Thanks for subscribing!"}'
post/mass-messagesscope: messages

Send one message to a segment of fans

Delivered as individual messages (a locked message unlocks per fan), only to fans you may chat with. 10 per hour.

curl -s https://meumore.com/api/v1/mass-messages -H "Authorization: Bearer $MEUMORE_KEY" -H 'Content-Type: application/json' -d '{"segment":{"type":"all_subscribers"},"text":"New post is up!"}'
get/mass-messages/{id}scope: messages

Status and counts of a mass message

curl -s https://meumore.com/api/v1/mass-messages/$MASS_ID -H "Authorization: Bearer $MEUMORE_KEY"
get/subscribersscope: fans

Your subscribers with lifetime spend

curl -s https://meumore.com/api/v1/subscribers?status=active -H "Authorization: Bearer $MEUMORE_KEY"
get/fans/{id}scope: fans

A fan: subscription status, spend, last message

curl -s https://meumore.com/api/v1/fans/$FAN_ID -H "Authorization: Bearer $MEUMORE_KEY"
get/earningsscope: earnings

Earnings by source and currency, and balances

curl -s https://meumore.com/api/v1/earnings?from=2026-09-01T00:00:00Z -H "Authorization: Bearer $MEUMORE_KEY"
get/transactionsscope: earnings

Ledger rows, one per payment (20% platform fee, 80% yours)

curl -s https://meumore.com/api/v1/transactions?source=tip -H "Authorization: Bearer $MEUMORE_KEY"
get/payoutsscope: earnings

Your payout requests

curl -s https://meumore.com/api/v1/payouts -H "Authorization: Bearer $MEUMORE_KEY"
post/payoutsscope: earnings

Request a payout of the full available balance in a currency

curl -s https://meumore.com/api/v1/payouts -H "Authorization: Bearer $MEUMORE_KEY" -H 'Content-Type: application/json' -d '{"currency":"INR"}'
get/webhooksscope: webhooks

Your webhook endpoints

curl -s https://meumore.com/api/v1/webhooks -H "Authorization: Bearer $MEUMORE_KEY"
post/webhooksscope: webhooks

Add a webhook endpoint (returns its signing secret once)

curl -s https://meumore.com/api/v1/webhooks -H "Authorization: Bearer $MEUMORE_KEY" -H 'Content-Type: application/json' -d '{"url":"https://example.com/meumore","events":["subscriber.new","tip.received"]}'
delete/webhooks/{id}scope: webhooks

Delete a webhook endpoint

curl -s -X DELETE https://meumore.com/api/v1/webhooks/$WEBHOOK_ID -H "Authorization: Bearer $MEUMORE_KEY"

Uploading and scheduling a post

  1. POST /media/uploads with the content type and size: you get an upload_id and a PUT URL.
  2. PUT the file bytes to that URL with the returned headers.
  3. POST /media with the upload_id, caption, visibility and an optional future publish_at. Scheduled posts are hidden everywhere until then; a job publishes them every minute and sends media.published.

Webhooks

Add an endpoint with POST /webhooks and the events you want. The response includes the endpoint's signing secret (whsec_…), shown once. We POST each event as JSON:

POST /your/endpoint
Content-Type: application/json
X-MeuMore-Event: tip.received
X-MeuMore-Signature: t=1727700000,v1=<hex HMAC-SHA256(secret, "1727700000." + raw body)>

{
  "id": "evt_0123456789abcdef01234567",
  "type": "tip.received",
  "created_at": "2026-09-30T12:00:00.000Z",
  "data": {
    "order_id": "uuid",
    "message_id": "uuid",
    "fan_id": "uuid",
    "amount_minor": 10000,
    "currency": "INR"
  }
}
  • Respond with any 2xx within 10 seconds. Anything else, or a timeout, is a failure.
  • Failures are retried with exponential backoff, up to 8 attempts in total: after 1 min, 4 min, 16 min, 1 h 4 min, 4 h 16 min, 12 h, 12 h.
  • An endpoint is disabled after 50 consecutive failed attempts. Delete it and add it again to re-enable.
  • Retries of an event keep the same id: use it to deduplicate. Events may arrive out of order.
  • Endpoints must be public https URLs; private, loopback and link-local addresses are refused.

Verifying signatures (Node.js)

Compute the HMAC over the raw request body (before JSON parsing) and compare in constant time:

import crypto from 'node:crypto';
import http from 'node:http';

const SECRET = process.env.MEUMORE_WEBHOOK_SECRET; // whsec_… from POST /webhooks

function verify(rawBody, header, toleranceSeconds = 300) {
  // header: "t=1727700000,v1=5f2b…"
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=', 2)));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false; // stale: possible replay
  const expected = crypto.createHmac('sha256', SECRET).update(`${t}.${rawBody}`).digest();
  const got = Buffer.from(parts.v1 ?? '', 'hex');
  return got.length === expected.length && crypto.timingSafeEqual(got, expected);
}

http.createServer((req, res) => {
  let raw = '';
  req.on('data', (c) => (raw += c));
  req.on('end', () => {
    if (!verify(raw, req.headers['x-meumore-signature'] ?? '')) {
      res.writeHead(400).end('bad signature');
      return;
    }
    const event = JSON.parse(raw); // { id, type, created_at, data }
    // Deduplicate on event.id: a retry of the same event has the same id.
    console.log(event.type, event.data);
    res.writeHead(200).end('ok');
  });
}).listen(8080);

Events

subscriber.new

A fan subscribed (first payment of a monthly subscription or a 30-day pass).

{
  "data": {
    "subscription_id": "uuid",
    "fan_id": "uuid",
    "mode": "auto",
    "current_period_end": "2026-10-30T12:00:00.000Z",
    "amount_minor": 14900,
    "currency": "INR"
  }
}

subscriber.renewed

A subscription renewed, or a returning fan paid again.

{
  "data": {
    "subscription_id": "uuid",
    "fan_id": "uuid",
    "mode": "auto",
    "current_period_end": "2026-11-30T12:00:00.000Z",
    "amount_minor": 14900,
    "currency": "INR"
  }
}

subscriber.cancelled

Auto-renew stopped (fan cancelled, payment halted or plan completed). Access runs to access_until.

{
  "data": {
    "subscription_id": "uuid",
    "fan_id": "uuid",
    "reason": "cancelled_by_fan",
    "access_until": "2026-10-30T12:00:00.000Z"
  }
}

subscriber.expired

A subscription ended.

{
  "data": {
    "subscription_id": "uuid",
    "fan_id": "uuid",
    "ended_at": "2026-10-30T12:00:00.000Z"
  }
}

purchase.created

A fan bought one of your paid posts.

{
  "data": {
    "order_id": "uuid",
    "media_id": "uuid",
    "fan_id": "uuid",
    "amount_minor": 4900,
    "currency": "INR"
  }
}

tip.received

A fan tipped you in chat.

{
  "data": {
    "order_id": "uuid",
    "message_id": "uuid",
    "fan_id": "uuid",
    "amount_minor": 10000,
    "currency": "INR"
  }
}

message.received

A fan sent you a message (after moderation, for photos and videos).

{
  "data": {
    "message_id": "uuid",
    "conversation_id": "uuid",
    "fan_id": "uuid",
    "kind": "text",
    "created_at": "2026-09-30T12:00:00.000Z"
  }
}

message.unlocked

A fan unlocked one of your locked messages.

{
  "data": {
    "order_id": "uuid",
    "message_id": "uuid",
    "fan_id": "uuid",
    "amount_minor": 14900,
    "currency": "INR"
  }
}

media.approved

A post or chat media item passed moderation.

{
  "data": {
    "media_id": "uuid",
    "kind": "image",
    "context": "gallery",
    "visibility": "subscribers",
    "published": true
  }
}

media.rejected

A post or chat media item was rejected in review.

{
  "data": {
    "media_id": "uuid",
    "kind": "image",
    "context": "gallery",
    "visibility": "free",
    "published": true
  }
}

media.published

A scheduled post went live.

{
  "data": {
    "media_id": "uuid",
    "kind": "image",
    "visibility": "subscribers",
    "status": "approved",
    "publish_at": "2026-10-01T18:00:00.000Z"
  }
}

payout.updated

A payout was requested, paid or rejected.

{
  "data": {
    "payout_id": "uuid",
    "status": "paid",
    "amount_minor": 250000,
    "currency": "INR",
    "note": "UTR 1234",
    "settled_at": "2026-10-02T10:00:00.000Z"
  }
}

Partners

For companies working with MeuMore. Three parts: invites (your server asks MeuMore to email an invitation to someone who agreed to receive it), demo users (AI demo profiles for testing) and partner apps (a MeuMore user lets your app act for them, for example to add AI-edited profile photos). Base URL https://meumore.com/api/partner/v1 · OpenAPI 3.1 (openapi.json). Partner keys and apps are issued by MeuMore; contact us through the Grievance page.

Invites

Authenticate with your partner key: Authorization: Bearer mm_partner_<id>_<secret>. We store only a hash of it. Keep it on your server.

Consent rules (read these first)

  • Only invite people who agreed, in words that name MeuMore, to receive an invitation from MeuMore. "Offers from our partners" or general marketing consent is not enough.
  • Every request must include consent: source (the form or page), captured_at (when) and text (the exact wording they agreed to). If consent.text doesn't mention MeuMore the request fails with 422 consent_not_specific and nothing is sent. We keep the consent record with the invite.
  • We send at most one invitation per address every 30 days, never to existing members, and never again to anyone who unsubscribed (a global list that applies to every partner).
  • Every email comes from MeuMore, names you as the source, and has a one-click unsubscribe.
  • Statuses: sent → clicked (opened the join link) → signed_up (joined with Google) → profile_complete (added a photo and finished their profile); or unsubscribed / bounced.
  • If someone joins from the link with a different Google address, the invite still counts and email_mismatch is true.
  • 201 created. 409 already_invited when you invited this address in the last 30 days (or it joined through your invite): the body has that invite's id and status and no email is sent. 409 not_invitable when the address unsubscribed, bounced, is already a member or was invited recently by someone else: we don't say which.
  • Limits per partner: 1000 invites per hour, 20000 per day and 600 API requests per minute (429 rate_limited).
post/api/partner/v1/invitespartner key

Send an invitation email from MeuMore to one person who agreed to receive it.

consent is required and consent.text must name MeuMore (422 otherwise). 409 when you invited this address in the last 30 days (with that invite's id and status), or when it can't be invited (unsubscribed, bounced, already a member, or invited recently by someone else; the reason isn't disclosed). Limits: 1000 per hour and 20000 per day.

curl -s https://meumore.com/api/partner/v1/invites -H "Authorization: Bearer $MEUMORE_PARTNER_KEY" \
  -H "Content-Type: application/json" -d '{
    "email": "[email protected]", "name": "Ana", "country": "BR",
    "consent": {"source": "oxbo.in/signup", "captured_at": "2026-09-30T10:00:00Z",
                "text": "I agree that Oxbo may share my email with MeuMore so MeuMore can send me an invitation."},
    "utm": {"source": "oxbo", "medium": "partner", "campaign": "sept"}, "external_id": "lead_123"
  }'
get/api/partner/v1/invitespartner key

Your invites, newest first, with cursor pagination.

curl -s "https://meumore.com/api/partner/v1/invites?status=signed_up" -H "Authorization: Bearer $MEUMORE_PARTNER_KEY"
get/api/partner/v1/invites/{id}partner key

One invite's status. Other partners' invites are 404.

curl -s https://meumore.com/api/partner/v1/invites/$INVITE_ID -H "Authorization: Bearer $MEUMORE_PARTNER_KEY"

Demo users (AI demo profiles)

With the demo scope you can create AI demo profiles for testing MeuMore, and read and answer the chats members send them. Demo profiles never sign in, get no emails, and never count in member numbers, stats, earnings or attribution.

Who sees demo profiles (set by MeuMore)

  • Testers only (the default): only MeuMore admins and testers see them, and can like, match, chat and use test payments with them. Nobody else sees them anywhere.
  • Labeled: every member sees them, always marked with an "AI" badge on every photo and avatar note, and can like, match and chat with them. Nobody except testers can pay a demo profile (subscribe, buy, unlock or tip: 403 demo_profile_payments).
  • Off: nobody sees them, testers included.
  • Payments with demo profiles only ever run with test payment keys.
  • Demo profiles like back whoever likes them (when that member may match with them), so a like is a match.
  • Replies you send through the API are stored as normal text messages from the demo profile and follow the normal chat rules.
post/api/partner/v1/demo-userspartner key

Create or update (by external_id) an AI demo profile.

multipart/form-data: the fields below, `photos` (1-5 image files; replaces all current photos on update) and `ai_enhanced` (once for all photos, or once per photo in order). Or JSON with `photos: [{upload_id, ai_enhanced}]` from POST /demo-users/uploads. Creating needs name, age (18+), gender, interested_in, looking_for and photos; updates take any subset. Photos go through moderation (rejected: 422). The profile is onboarded, has no Google identity and can never sign in. 201 created, 200 updated.

curl -s https://meumore.com/api/partner/v1/demo-users -H "Authorization: Bearer $MEUMORE_PARTNER_KEY" \
  -F external_id=demo_ana -F name="Ana" -F age=29 -F gender=woman -F interested_in=man \
  -F looking_for=both -F city="São Paulo" -F country=BR -F lat=-23.55 -F lng=-46.63 -F bio="Coffee and hiking" \
  -F [email protected] -F [email protected] -F ai_enhanced=true
get/api/partner/v1/demo-userspartner key

Your demo profiles, newest first.

delete/api/partner/v1/demo-userspartner key

Delete all your demo profiles with their photos, media and chats.

curl -s -X DELETE https://meumore.com/api/partner/v1/demo-users -H "Authorization: Bearer $MEUMORE_PARTNER_KEY"
post/api/partner/v1/demo-users/uploadspartner key

Presigned PUT for a demo profile photo (then POST /demo-users with photos: [{upload_id}]).

get/api/partner/v1/demo-users/{external_id}partner key

One demo profile.

delete/api/partner/v1/demo-users/{external_id}partner key

Delete one demo profile, its photos, media and chats.

get/api/partner/v1/demo-users/{external_id}/conversationspartner key

Chats members started with this demo profile, each with its latest 20 messages (oldest first). Fetched messages are marked read.

curl -s https://meumore.com/api/partner/v1/demo-users/demo_ana/conversations -H "Authorization: Bearer $MEUMORE_PARTNER_KEY"
post/api/partner/v1/demo-users/{external_id}/messagespartner key

Reply as the demo profile (text only). The same chat rules and rate limit (30 per minute) apply as for members.

curl -s https://meumore.com/api/partner/v1/demo-users/demo_ana/messages -H "Authorization: Bearer $MEUMORE_PARTNER_KEY" -H "Content-Type: application/json" \
  -d '{"conversation_id":"<id>","text":"Hi! Thanks for the message."}'

Partner apps (OAuth 2.0 with PKCE)

Your app gets a client_id, a client secret and exact redirect URIs from MeuMore. The user signs in with Google on MeuMore, sees your app's name and what it may do, and chooses Allow or Deny. They can disconnect your app at any time in Settings → Connected apps.

  1. Redirect the user to https://meumore.com/partner/authorize with response_type=code, client_id, redirect_uri (must match a registered URI exactly), scope (space-separated), state (required) and, recommended, code_challenge + code_challenge_method=S256.
  2. We redirect back to redirect_uri?code=…&state=…, or ?error=access_denied&state=…. Check state.
  3. POST the code to https://meumore.com/api/partner/oauth/token within 10 minutes. Codes work once. Send your client_secret (body or HTTP Basic), the PKCE code_verifier, or both: one of them is required.
  4. Call the user endpoints with Authorization: Bearer mmat_…. Access tokens last 90 days. Refresh with grant_type=refresh_token: you get a new access and refresh token and the old pair stops working.
import crypto from 'node:crypto';

// 1. Before redirecting the user: a random verifier, its S256 challenge and a state.
const verifier = crypto.randomBytes(32).toString('base64url');        // keep it (session), never send it here
const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
const state = crypto.randomBytes(16).toString('base64url');           // check it on the way back

const url = new URL('https://meumore.com/partner/authorize');
url.search = new URLSearchParams({
  response_type: 'code',
  client_id: process.env.MEUMORE_CLIENT_ID,
  redirect_uri: 'https://yourapp.example/meumore/callback',            // exactly as registered
  scope: 'photos:write profile:read',
  state,
  code_challenge: challenge,
  code_challenge_method: 'S256',
}).toString();
// redirect the user to url

// 2. On https://yourapp.example/meumore/callback?code=…&state=…  (or ?error=access_denied&state=…)
async function exchange(code) {
  const res = await fetch('https://meumore.com/api/partner/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: process.env.MEUMORE_CLIENT_ID,
      client_secret: process.env.MEUMORE_CLIENT_SECRET,               // server-side apps; omit in a public client
      code,
      redirect_uri: 'https://yourapp.example/meumore/callback',
      code_verifier: verifier,
    }),
  });
  return res.json(); // { access_token: "mmat_…", refresh_token: "mmrt_…", expires_in: 7776000, token_type: "Bearer", scope }
}
curl -s https://meumore.com/api/partner/oauth/token \
  -d grant_type=refresh_token -d client_id=$MEUMORE_CLIENT_ID \
  -d client_secret=$MEUMORE_CLIENT_SECRET -d refresh_token=$REFRESH_TOKEN

Scopes

  • profile:read · See your basic profile: first name, age, country and your photos
  • photos:write · Add or replace your profile photos (up to 5; every photo is checked by our moderation first)
  • media:write · Post photos and videos to your gallery as you (creator accounts only)

User endpoints

get/api/partner/v1/user/meuser token · any

The user who authorised your app, the granted scopes and (with profile:read) their basic profile.

curl -s https://meumore.com/api/partner/v1/user/me -H "Authorization: Bearer $USER_ACCESS_TOKEN"
post/api/partner/v1/user/photosuser token · photos:write

Add a profile photo (next free slot; max 5) or replace one.

multipart/form-data with `photo` (JPEG, PNG, WebP or HEIC, 10 MB max), optional `replace` (a photo id or "main") and `ai_enhanced` ("true" when the image was made or edited with AI). Or JSON {upload_id, replace?, ai_enhanced?} after POST /user/photos/uploads. Every photo is moderated: rejected images return 422, others may stay pending until reviewed. At 5 photos adding returns 400: replace one instead.

curl -s https://meumore.com/api/partner/v1/user/photos -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  -F [email protected] -F ai_enhanced=true
post/api/partner/v1/user/photos/uploadsuser token · photos:write

Presigned PUT for a large profile photo; then POST /user/photos {"upload_id": …}.

post/api/partner/v1/user/media/uploadsuser token · media:write

Presigned PUT for a gallery photo or video.

curl -s https://meumore.com/api/partner/v1/user/media/uploads -H "Authorization: Bearer $USER_ACCESS_TOKEN" -H "Content-Type: application/json" \
  -d '{"content_type":"image/jpeg","size":123456}'
post/api/partner/v1/user/mediauser token · media:write

Publish an upload to the user's gallery, as the user.

Same processing, moderation and scheduling as posts made on the site. The user must be a creator (creator fee paid), otherwise 403 not_a_creator; paid needs tier_id.

curl -s https://meumore.com/api/partner/v1/user/media -H "Authorization: Bearer $USER_ACCESS_TOKEN" -H "Content-Type: application/json" \
  -d '{"upload_id":"<upload_id>","content_type":"image/jpeg","caption":"Studio shoot","ai_enhanced":true}'

ai_enhanced

Set ai_enhanced: true on photos and posts your app generated or edited with AI. MeuMore shows a small "AI-enhanced" label on them in the profile photos and the gallery, and returns ai_enhanced in the API. Always set it when AI changed how someone looks, so people on MeuMore know what they are seeing. The max-5 photo rule and moderation apply as usual.

Errors and limits

API errors are JSON {"error":{"code","message"}}; the token endpoint uses OAuth's {"error","error_description"}.

  • 400 invalid_request, invalid_json · 401 invalid_partner_key, invalid_token, token_expired, token_revoked, app_disabled · 403 partner_disabled, insufficient_scope, not_a_creator, account_disabled, demo_profile_payments · 404 not_found · 409 already_invited, not_invitable · 413 payload_too_large · 415 unsupported_media_type · 422 consent_required, consent_invalid, consent_not_specific, unprocessable (content rejected by moderation) · 429 rate_limited · 503 email_failed (retry later).
  • Token endpoint: invalid_client (401), invalid_grant (400: unknown, used or expired code, wrong redirect_uri or verifier), unsupported_grant_type.
  • Limits: partner key 600 requests/min; invites 1000/hour and 20000/day; user tokens 60 requests/min each; photo and media uploads 20/hour per user; token endpoint 30/min per app.