読み込み中
API Reference

メンエスなう 連携 API 仕様書

貴社サービスからメンエスなうのタイムラインへ投稿いただくための API です。認可には X / Bluesky と同様の OAuth 2.0 Authorization Code フロー(PKCE 必須)を採用しています。店舗・セラピストご本人が利用する場合は、管理画面から発行できる個人 API トークンにも対応しています。

1. はじめに(連携の流れ)

本 API 連携は審査制のクローズドな提供です。ご利用開始までの流れは次のとおりです。

  1. メンエスなうのアカウントを作成し、ログインします(一般会員としての登録で差し支えありません)
  2. お申し込みフォームより申請します(ログインが必要です)
  3. 審査のうえ、承認後に連携アプリを発行し、ご連絡します
  4. マイページの「API連携(開発者)」にて client_id を確認し、client_secret を貴社ご自身で発行します(運営側では Secret を保持しません)
  5. 後述の認可フローでユーザーの許可を取得し、アクセストークンを発行します
  6. アクセストークンを用いて API を呼び出します(プロフィール取得・投稿)

本 API では、ユーザー(セラピスト/店舗アカウント)の許可に基づき、当該ユーザーのタイムラインへ投稿できます。あわせて、ユーザーが連携済みの X / Bluesky への同時配信にも対応しています。

2. 認可フロー(OAuth 2.0 + PKCE)

本 API では PKCE(S256)を必須としています。code_challenge を含まないリクエストは拒否されます。リダイレクト URI に指定できるのは https の URL のみです。

① 認可リクエスト

GET https://men-esthe.co.jp/oauth/authorize/ ?client_id={CLIENT_ID} &response_type=code &redirect_uri={REDIRECT_URI} &scope=profile:read post:write &state={RANDOM_STATE} &code_challenge={CODE_CHALLENGE} &code_challenge_method=S256

ユーザーがログインし、同意画面で許可すると、redirect_uri に code と state が付与されて返却されます。

② トークン交換

POST https://men-esthe.co.jp/oauth/token/ Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code={CODE} &redirect_uri={REDIRECT_URI} &client_id={CLIENT_ID} &client_secret={CLIENT_SECRET} &code_verifier={CODE_VERIFIER}
{ "access_token": "...", "expires_in": 3600, "token_type": "Bearer", "scope": "profile:read post:write", "refresh_token": "..." }

③ トークン更新(リフレッシュ)

  • アクセストークンの有効期限: 1 時間
  • リフレッシュトークンの有効期限: 90 日
  • ローテーション方式: 更新のたびに新しい refresh_token が発行され、従前のトークンは失効します。更新のつど、必ず新しい値を保存してください。失効済みトークンが再利用された場合、当該ユーザーのトークンはすべて無効化されます。
  • 更新処理の直列化: 同一ユーザーのトークンを複数のプロセスが同時に更新した場合、旧トークンの再利用と判定され、トークン全体が失効するおそれがあります。同一ユーザーの更新処理は同時に 1 つに制限(ロック等による直列化)することを推奨します。
  • 猶予期間(120 秒): ローテーション直後の旧 refresh_token は、120 秒間に限り再利用が許容されます。一時的な競合やリトライによって直ちに失効することはありません。
POST https://men-esthe.co.jp/oauth/token/ grant_type=refresh_token &refresh_token={REFRESH_TOKEN} &client_id={CLIENT_ID} &client_secret={CLIENT_SECRET}

④ トークン失効(RFC 7009)

POST https://men-esthe.co.jp/oauth/revoke_token/ token={ACCESS_OR_REFRESH_TOKEN} &client_id={CLIENT_ID} &client_secret={CLIENT_SECRET}

3. 個人 API トークン(店舗・セラピスト向け)

店舗・セラピストご本人がご自身のアカウントで API を利用する場合は、OAuth の認可フローを実装しなくても、管理画面から発行できる個人 API トークンをご利用いただけます。お申し込み・審査は不要です。

  • 発行: 管理画面の「API連携」(/manage/api-tokens/)からトークン名を付けて発行します。平文のトークンは発行直後の画面でのみ表示されますので、必ず控えてください
  • スコープ: profile:read profile:write post:write(固定。すべての API を利用できます)
  • 有効期限: 使っている限り自動延長されます(API の利用が成功するたび、その時点から 1 年に更新)。1 年間利用が無いトークンは失効します。1 アカウントあたり最大 10 件
  • 失効: 同じ画面からいつでも失効できます(即時無効)。漏えいの疑いがある場合は、すぐに失効のうえ再発行してください
GET https://men-esthe.co.jp/api/v1/me/ Authorization: Bearer {PERSONAL_TOKEN}

以降の API リファレンス・レート制限・エラーの仕様は、OAuth で発行したアクセストークンと共通です(トークンの更新・失効エンドポイントは使用しません)。

4. スコープ

スコープ内容対象エンドポイント
profile:read連携プロフィールの取得GET /api/v1/me/
profile:writeセラピストプロフィールの更新PATCH /api/v1/profiles/{puid}/
post:writeタイムラインへの投稿、および投稿した内容の配信ステータス確認POST /api/v1/posts/
POST /api/v1/posts/bulk/
GET /api/v1/posts/{puid}/

※ GET /api/v1/posts/{puid}/ は参照系のエンドポイントですが、自アプリが作成した投稿の配信結果を確認する用途に限定されるため(他アプリの投稿は参照できません)、post:write スコープに含めています。

複数の機能を利用する場合は、認可リクエストで必要なスコープをスペース区切りで指定してください(例: scope=profile:read post:write)。個人 API トークンには 3 つすべてのスコープが付与されています。

5. API リファレンス

すべてのエンドポイントで Authorization: Bearer {ACCESS_TOKEN} ヘッダが必要です。投稿が可能なのはセラピスト/店舗アカウントのトークンに限られます。

GET /api/v1/me/ scope: profile:read

当該トークンで投稿可能なプロフィールの一覧を返します。投稿時に therapist / store フィールドへ指定する puid は、本エンドポイントで取得できます。

{ "role": "therapist", "profiles": [ { "type": "therapist", "puid": "AbC...", "name": "あおい", "connected_platforms": ["x"] }, { "type": "therapist", "puid": "XyZ...", "name": "ひなた", "connected_platforms": [] } ] }

connected_platforms は連携済みの外部プラットフォームを示します。crosspost で指定した場合も、ここに含まれるプラットフォームにのみ配信されます。

POST /api/v1/posts/ scope: post:write

タイムラインへ投稿を作成します。Content-Type には application/json を、画像を添付する場合は multipart/form-data を使用します。

{ "content": "本日 18 時から出勤します。", "crosspost": ["x", "bluesky"], "attach_images": true, "therapist": "(任意)投稿主体セラピストの puid", "store": "(任意)投稿主体店舗の puid" }
フィールド必須内容
content✅本文(最大 1,000 文字)
images-multipart/form-data で最大 4 枚まで添付可能(JPEG / PNG / GIF / WebP、1 枚あたり 10MB まで)
crosspost-X / Bluesky への同時配信。連携済みのプラットフォームにのみ配信されます(/me で事前に確認できます)
attach_images-クロスポスト時に画像を添付するかどうか(既定値: true)
therapist / store-複数プロフィールを保有する場合の投稿主体(puid)。未指定の場合は先頭のプロフィールが使用されます

画像を添付する場合は multipart/form-data の images フィールドに添付し、その他のフィールドはフォームフィールドとして送信してください。この場合、crosspost はカンマ区切りの文字列(例: "x,bluesky")でも指定できます。

レスポンス(201)

{ "puid": "AbCdEf...", "content": "本日 18 時から出勤します。", "post_type": "text", "is_published": true, "created_at": "2026-07-06T18:00:00+09:00", "images": [], "crosspost": { "requested": ["x", "bluesky"], "dispatched": ["x"], "connected_platforms": ["x"], "held_for_review": false } }
  • is_published: false の場合、運営による内容確認の完了後に公開されます。公開までの間、クロスポストは配信されません(held_for_review: true)。
  • dispatched は、配信キューへの投入が確定したプラットフォームを示します。未連携のプラットフォームは自動的にスキップされます。
POST /api/v1/posts/bulk/ scope: post:write

複数の投稿を 1 リクエストで作成します(最大 10 件)。Content-Type は application/json のみで、画像の添付には対応していません(画像付きの投稿は POST /api/v1/posts/ をご利用ください)。

{ "posts": [ { "content": "1 件目の投稿", "crosspost": ["x", "bluesky"] }, { "content": "2 件目の投稿" } ] }

各要素には単発投稿(POST /api/v1/posts/)と同じフィールド(content / crosspost / therapist / store)を指定できます。クロスポストも件ごとに指定可能です。

レスポンス(200)

{ "total": 2, "created": 2, "results": [ { "index": 0, "status": "created", "puid": "AbC...", "is_published": true, "crosspost": { "dispatched": ["x"] } }, { "index": 1, "status": "created", "puid": "XyZ...", "is_published": true, "crosspost": { "dispatched": [] } } ] }
  • 部分成功方式です。件ごとに独立して処理され、一部が失敗してもレスポンスは常に 200 で返ります。必ず results[i].status(created / error)で件別に成否を確認してください。
  • 失敗した要素には code と詳細(errors または message)が含まれます。例: { "index": 1, "status": "error", "code": "invalid_request", "errors": ... }
  • 日次投稿上限は作成に成功した件数だけ消費します。上限に達した以降の要素は code: throttled で失敗します。
  • 同一プロフィールで同じ本文の投稿が直前にある要素は code: duplicate_post で失敗し、puid に既存の投稿が入ります(同一リクエスト内に同じ本文を複数含めた場合も 1 件目のみ作成されます)。
  • バースト制限(10 回 / 分)に対しては、1 リクエストで 1 回分として計上されます。
GET /api/v1/posts/{puid}/ scope: post:write

投稿の公開状態とクロスポストの配信結果を返します。配信は非同期に実行されるため、最終的な結果は本エンドポイントへのポーリングによりご確認ください。

{ "puid": "AbCdEf...", "is_published": true, "syndications": [ { "platform": "x", "status": "success", "external_url": "https://x.com/xxx/status/123", "published_at": "2026-07-06T18:00:05+09:00", "error_message": "" } ] }

status は pending(配信中)/success/failed のいずれかです。配信に失敗した場合は自動的にリトライされます。

ポーリングに関する注意: 配信はキュー投入後に開始されるため、投稿直後は syndications が空配列となる場合があります。空配列は配信失敗を意味しません。投稿レスポンスの crosspost.dispatched に含まれる各プラットフォームについて、syndications に success または failed の結果が揃うまでポーリングしてください(通常は数秒程度、リトライが発生した場合は最大で数十分を要することがあります)。
PATCH /api/v1/profiles/{puid}/ scope: profile:write

セラピストプロフィールを更新します(application/json)。リクエストに含めたフィールドのみ更新されます。更新できるのは、トークンユーザーが当該セラピストご本人、または所属店舗のオーナーである場合に限られます(それ以外は 404)。

{ "description": "新しい自己紹介文", "age": 25, "height": 158, "cup": "D" }
フィールド内容
description自己紹介(最大 500 文字)
age / height / bust / waist / hip数値(0〜999)。null を指定すると未設定に戻せます
cupカップ(最大 5 文字)

レスポンス(200)

{ "puid": "AbC...", "name": "あおい", "description": "新しい自己紹介文", "age": 25, "height": 158, "bust": null, "waist": null, "hip": null, "cup": "D", "updated_at": "2026-08-10T12:00:00+09:00" }
  • セラピスト名(stage_name)は本 API からは変更できません(指定しても無視されます)。名称の変更は管理画面から行ってください。
  • 更新対象の puid は GET /api/v1/me/ で確認できます。店舗オーナーの場合、所属セラピストの puid は管理画面のセラピスト一覧から確認してください。

6. レート制限

制限値
投稿(バースト)10 回 / 分(一括投稿は 1 リクエストで 1 回分)
投稿(日次上限)既定 50 回 / 日(連携ユーザーごと。一括投稿は作成成功数で計上)
プロフィール更新30 回 / 分
読み取り60 回 / 分

投稿の日次上限は、連携ユーザー(店舗/セラピスト)1 名あたりで計上されます。投稿主体ごとに独立した枠が割り当てられるため、複数のユーザーと連携している場合は、それぞれに日次上限が適用されます。上限値は連携内容に応じて個別に調整いたしますので、大量の投稿を予定される場合は申請時に想定投稿量をお知らせください。制限を超過した場合は、HTTP 429 とともに Retry-After ヘッダを返します。上限値は運用状況に応じて予告なく変更される場合があります。

7. エラー

エラーレスポンスは、機械可読な code フィールドを含む JSON 形式で返されます。code はすべてのエラーレスポンスに必ず含まれます。付随する詳細フィールドは、code の種別により 2 通りの形式があります。

  • invalid_request(400、バリデーションエラー)の場合は、errors にフィールドごとの詳細が含まれます。
  • 上記以外の場合は、message に人間可読な説明文が含まれます。

バリデーションエラー(invalid_request)の例:

{ "code": "invalid_request", "errors": { "content": ["この項目は必須です。"] } }

その他のエラーの例:

{ "code": "ip_not_allowed", "message": "許可されていない IP アドレスからのアクセスです。" }
codeHTTP意味
not_authenticated401アクセストークンが未指定、または無効
permission_denied403トークンのスコープが不足している(例: post:write を持たないトークンでの投稿)
invalid_request400バリデーションエラー(errors にフィールドごとの詳細)
role_not_allowed403セラピスト/店舗アカウント以外のトークンによるアクセス
app_suspended403連携アプリが停止中
ip_not_allowed403許可されていない IP アドレスからのアクセス
no_profile403投稿可能なプロフィールが存在しない
throttled429レート制限の超過(Retry-After ヘッダを参照)
not_found404投稿が存在しない、または他ユーザーの投稿である
duplicate_post409同一プロフィール・同一本文の投稿が直前に存在する。レスポンスの puid が既存の投稿を指します(再送時の突き合わせにご利用ください)。日次上限は消費しません

8. ご利用にあたって

  • 連携アプリ(OAuth)は審査制のクローズドな提供です。ご利用には事前のお申し込みと審査が必要です。店舗・セラピストご本人がご自身のアカウントで利用する場合は、審査不要の個人 API トークンをご利用いただけます。
  • サーバー間通信専用です。ブラウザからの直接呼び出し(CORS)には対応していません。client_secret およびアクセストークンは必ずサーバー側で保持し、フロントエンド(SPA・モバイルアプリ等)には埋め込まないでください。
  • 投稿(POST /api/v1/posts/ および /api/v1/posts/bulk/)は冪等ではありません。タイムアウトが発生した場合は、再送前に GET /api/v1/posts/{puid}/ で投稿結果をご確認ください。確認せずに再送した場合、二重投稿となるおそれがあります。
  • スパム、なりすまし、無断転載など、メンエスなうの利用規約および投稿ガイドラインに反する利用は禁止されています。違反が確認された場合、連携を停止することがあります。
  • 本仕様書の対象は /api/v1/ 系エンドポイントです。後方互換性を維持するため、破壊的な変更は新しいバージョンとして提供します。
  • レート制限値および仕様は、予告なく変更される場合があります。本 API は無保証で提供されます。
  • ご不明な点がございましたら、担当者または連絡先メールアドレスまでお問い合わせください。

ホーム画面に追加

アプリみたいに使えます