# Sharingtools REST API > Sharingtools is a multi-tenant Instagram/TikTok/X automation SaaS platform. This REST API exposes all core platform operations: account management (including Instagram login with challenge/2FA/IMAP auto-code support), post scheduling, Reactions Pro automation, Repost Pro automation, statistics, captions, proxies, wallet, and admin controls. Base URL: https://sharingtools.services/api/v1 Auth: Bearer token — header `Authorization: Bearer sht_` or `X-API-Key: sht_` Content-Type for request bodies: application/json Response format: JSON `{"result":1,"message":"...","data":{...}}` (success) or `{"result":0,"message":"...","errors":{...}}` (error) Pagination: query params `?page=1&per_page=20` (max per_page=100), meta block in list responses ## HTTP Status Codes 200 OK | 201 Created | 202 Accepted (retry) | 400 Bad request | 401 Unauthorized | 403 Forbidden | 404 Not found | 405 Method not allowed | 409 Conflict | 410 Gone (expired) | 422 Unprocessable entity | 500 Server error | 503 Plugin unavailable ## Authentication POST /api/v1/auth/token — no auth required Body: {"username":"str","password":"str"} Returns 200 with key_prefix/key_id if key exists, or 201 with raw api_key (shown once only) All other endpoints require: Authorization: Bearer sht_ ## Endpoints ### GET /api/v1/dashboard Returns account summary: user profile, account counts, post counts, caption count, wallet balance, recent posts, api_key info. ### GET /api/v1/user Returns: id, email, username, firstname, lastname, account_type, is_active, is_expired, package_id, expire_date, is_online, last_activity_time, date_joined ### PUT|PATCH /api/v1/user Updatable fields: firstname (str), lastname (str), email (str) ### GET /api/v1/user/subscription Returns: package (object), expire_date, is_expired, package_id, package_subscription ### GET /api/v1/accounts Query: page, per_page, login_required (0|1), slave (0|1), search (str) Returns list: id, user_id, instagram_id, username, proxy, mobile_proxy, login_required, slave, child, rpa, engagement_mod, counter, last_login, date ### POST /api/v1/accounts Body: username (str, required), password (str, required), proxy (str, optional) Creates account record directly (no live Instagram login — use /accounts/login for fresh logins) Returns 201 with account object | 409 if username already exists | 403 if account limit reached ### GET /api/v1/accounts/{id} Returns single account. 404 if not owned by caller. ### PUT|PATCH /api/v1/accounts/{id} Updatable: proxy (str), mobile_proxy (str), login_required (0|1), password (str) ### DELETE /api/v1/accounts/{id} Permanently deletes account. ### POST /api/v1/accounts/login Live Instagram login. Creates account (np_accounts INSERT) on success, or updates existing account on re-login. Re-login: if username already exists for this user AND login_required=1, the existing np_accounts row is updated (password, instagram_id, proxy, last_login, login_required→0). Account-limit check is skipped for re-logins. Body: username str required Instagram username password str required Instagram password proxy str optional Proxy string (http://user:pass@host:port or socks5://...) twofa_secret str optional TOTP base-32 secret for auto-2FA resolution imap_server str optional* IMAP hostname (e.g. imap.gmail.com) imap_port int optional IMAP port, default 993 imap_username str optional* IMAP login / email address imap_password str optional* IMAP password (stored Defuse-encrypted, never returned) (* imap_server, imap_username, imap_password are all-or-nothing; IMAP connection tested immediately) Responses: 201 — new account created: {"result":1,"message":"Account added successfully.","data":{account_object}} 200 — existing account re-authenticated: {"result":1,"message":"Account re-authenticated successfully.","data":{account_object}} 200 — challenge required: {"result":1,"data":{"needs_challenge":true,"challenge_token":"<64hex>","challenge_type":"email|sms|unknown","expires_in":600,"relogin":bool}} 401 — wrong password 403 — checkpoint required or account limit reached (new accounts only) 404 — Instagram account not found 409 — account exists AND login_required=0 (already active, no re-login needed) 422 — validation error or IMAP connection failed 503 — network/Instagram error ### POST /api/v1/accounts/challenge Complete a pending Instagram challenge. On success creates (INSERT) or updates (UPDATE) the np_accounts row. Body: challenge_token str required Token from /accounts/login (valid 10 minutes) code str optional* 6-digit verification code auto_imap bool optional If true, fetch code automatically via stored IMAP credentials (* code required unless auto_imap:true; auto_imap requires IMAP credentials were supplied at login) IMAP auto-fetch behavior: server sleeps 5s, scans all IMAP folders for email from @instagram.com received after challenge_at timestamp, extracts 6-digit code via regex. Responses: 201 — new account created: {"result":1,"message":"Account added successfully.","data":{account_object}} 200 — existing account re-authenticated: {"result":1,"message":"Account re-authenticated successfully.","data":{account_object}} 202 — {"result":0,"message":"Instagram email not yet received. Retry in a few seconds.","retry":true} — retry same request 400 — wrong code 404 — invalid/expired challenge_token 410 — token expired (10 min TTL), restart from /accounts/login 422 — auto_imap:true but no IMAP credentials stored, or validation error 503 — Instagram network error Challenge state stored in np_api_login_challenges: token, user_id, username, password_enc (Defuse), proxy, api_path, twofa_secret, imap_server, imap_port, imap_username, imap_password_enc (Defuse), challenge_at (unix ts), created_at, expires_at. ### GET /api/v1/posts Query: page, per_page, status (saved|scheduled|published|failed), account_id (int), is_scheduled (0|1) Returns list: id, user_id, account_id, status, type, caption, first_comment, location, link, media_ids, is_scheduled, schedule_date, publish_date, create_date ### POST /api/v1/posts Body: account_id (int, required), caption (str), type (str), first_comment (str), location (str), link (str), media_ids (str, comma-sep IDs), is_scheduled (bool), schedule_date (Y-m-d H:i:s) Returns 201 with post object. ### GET /api/v1/posts/{id} Single post. 404 if not owned. ### PUT|PATCH /api/v1/posts/{id} Updatable: caption, type, first_comment, location, link, media_ids, is_scheduled, schedule_date Cannot update published posts. ### DELETE /api/v1/posts/{id} Deletes post. ### GET /api/v1/statistics Returns: accounts (total, active, login_required, slave), posts (total, scheduled, published, failed), captions (total), growth (total_followers, total_followings, followers_gained_today, tracked_accounts — only if Stats plugin installed) ### GET /api/v1/statistics/accounts/{id} Returns detailed stats + 30-day growth history for one account. ### GET /api/v1/packages Returns list: id, title, monthly_price, annual_price, modules (array), is_public, date ### GET /api/v1/packages/{id} Single package. ### GET /api/v1/proxies Query: country_code (str), page, per_page Returns list: id, proxy, country_code, use_count Note: proxies are shared (no user_id). ### GET /api/v1/captions Query: search (str), page, per_page Returns list: id, user_id, title, caption, date ### POST /api/v1/captions Body: title (str, required), caption (str, required) Supports spintax syntax: {option1|option2} ### GET /api/v1/captions/{id} ### PUT|PATCH /api/v1/captions/{id} Updatable: title, caption ### DELETE /api/v1/captions/{id} ### GET /api/v1/wallet Returns: balance (float), currency (str), user_id (int) ### GET /api/v1/wallet/transactions Query: type (credit|debit), status (pending|completed|failed), page, per_page Returns list: id, amount, type, status, description, payment_id, created, modified ### GET /api/v1/keys Returns current API key info: id, name, key_prefix, is_active, request_count, last_used_at, last_used_ip, created_at ### POST /api/v1/keys/generate Body: name (str, optional) Revokes all existing keys, generates new key. Returns 201 with raw api_key (shown once only). ### POST /api/v1/keys/revoke Revokes current key. Subsequent requests with it return 401. ## Reactions Pro Endpoints Requires: Reactions Pro plugin installed AND user plan includes "reactions" module. 503 if plugin not installed. 403 if plan doesn't include module. account_id in path = np_accounts.id (not Instagram numeric ID). ### GET /api/v1/reactions/schedules Returns list of all reaction schedules: id, account_id, account_username, instagram_id, is_active, is_running, is_executed, speed, daily_pause, daily_pause_from, daily_pause_to, messages, target (array of {type,value,id}), data, schedule_date, end_date, last_action_date, next_check_date, expiration_date Meta: count (int) ### GET /api/v1/reactions/schedules/{account_id} Single schedule for account. ### POST /api/v1/reactions/schedules/bulk/activate Activate all (or subset) of user's schedules. Sets is_active=1. Body (optional): {"account_ids":[5,10,15]} — omit to affect all schedules. Unknown/unowned IDs silently skipped, reported in "skipped" array. Returns: {"data":{"affected":N,"account_ids":[...],"skipped":[...]}} 404 if no valid schedules found. ### POST /api/v1/reactions/schedules/bulk/pause Pause all (or subset) of user's schedules. Sets is_active=0. Same body/response shape as bulk/activate. ### POST /api/v1/reactions/schedules/bulk/stop Hard-stop all (or subset) of user's schedules. Sets is_active=0 AND is_running=0. Use when a cron cycle is stuck (is_running=1 but not progressing). pause only prevents new cycles; stop also clears the running flag. Same body/response shape as bulk/activate. ### POST /api/v1/reactions/schedules/{account_id}/activate Starts reactions for one account. 404 if no schedule exists (create via web panel). 409 if already active. Returns: {"data":{"account_id":N,"is_active":true}} ### POST /api/v1/reactions/schedules/{account_id}/pause Pauses reactions for one account. 409 if already paused. Returns: {"data":{"account_id":N,"is_active":false}} ### GET /api/v1/reactions/targets/types Returns catalog of all valid target types. No params required. Returns: {"data":{"types":[{"type":"hashtag","description":"...","requires_id":false},...], "total":14}} Valid types: timeline, hashtag, hashtag_likers, keyword, keyword_likers, keyword_reels, keyword_reels_likers (requires_id=false), people, people_followers, people_followings, people_likers, location, location_likers (requires_id=true), user_id_list (requires_id=false). requires_id=true means a numeric Instagram ID must be supplied in the "id" field when adding the target. ### GET /api/v1/reactions/schedules/{account_id}/targets Returns list of targets configured for the schedule. Response: {"data":{"targets":[{"type":"hashtag","id":"photography","value":"photography"},...], "total":N}} ### POST /api/v1/reactions/schedules/{account_id}/targets Add a target to a schedule. 404 if schedule not found. Body: {"type":"str (required)", "value":"str (required for most types)", "id":"str (required if requires_id=true)"} For types where requires_id=false, id is auto-set equal to value if omitted. Returns 200 with updated targets array. ### DELETE /api/v1/reactions/schedules/{account_id}/targets Remove one or all targets of a given type from a schedule. Body: {"type":"str (required)", "value":"str (optional — omit to remove ALL targets of this type)"} Returns 200 with updated targets array. ### GET /api/v1/reactions/schedules/{account_id}/settings Returns full settings grouped: schedule, story_reactions, comment, likes, follow, unfollow, dm, reply_dm, welcome_dm, filters, language, reposts, close_friends, accelerators, activity_time, custom_delays, notifications, proxy, advanced ### PATCH /api/v1/reactions/schedules/{account_id}/settings Partial update — only sent fields are changed. 422 if unknown or read-only field sent. Returns: {"data":{"account_id":N,"updated_fields":[...]}} Writable settings fields (field → type → category): daily_pause bool schedule | daily_pause_from str(HH:MM) schedule | daily_pause_to str(HH:MM) schedule | expiration bool schedule | expiration_date str(Y-m-d H:i:s) schedule | is_pause_until bool schedule | is_reset_hl bool schedule | autopilot bool schedule | is_poll bool story_reactions | is_poll_slider bool story_reactions | is_quiz bool story_reactions | is_answer bool story_reactions | is_countdown bool story_reactions | is_masslooking bool story_reactions | fresh_stories bool story_reactions | is_story_likes bool story_reactions | like_stories_algorithm int story_reactions | like_stories_type int story_reactions | massvoting_interval float intervals | like_stories_interval float intervals | masslooking_interval float intervals | comment_interval int intervals | dm_interval int intervals | like_comments_interval int intervals | like_comments_speed int intervals | is_emoji bool story_reactions | emojis str story_reactions | emoji_speed int story_reactions | prorate_algorithm bool actions | prorate_reverse_looping bool actions | is_comment bool comment | comment_speed int comment | is_comment_filter bool comment | is_likes bool likes | likes_speed int likes | likes_per_user int likes | is_likes_timeline bool likes | is_c_likes bool likes | c_likes_speed int likes | is_follow bool follow | follow_speed int follow | follow_limit int follow | mute_type str follow | is_unfollow bool unfollow | unfollow_speed int unfollow | unfollow_skip_followers bool unfollow | unfollow_non_followers bool unfollow | auto_follow_unfollow bool unfollow | unfollow_interval int unfollow | is_dm bool dm | dm_speed int dm | dm_mute bool dm | dm_move_to_general bool dm | dm_only_text bool dm | dm_skip_following bool dm | dm_skip_following_request bool dm | dm_remove_from_list bool dm | is_reply_dm bool reply_dm | is_not_like_dm bool reply_dm | is_not_mute_dm bool reply_dm | is_only_text_dm bool reply_dm | is_welcome_dm bool welcome_dm | welcome_dm_mute bool welcome_dm | welcome_dm_only_text bool welcome_dm | welcome_dm_move bool welcome_dm | welcome_dm_speed int welcome_dm | post_min_likes int post_filter | post_max_likes int post_filter | post_min_views int post_filter | post_max_views int post_filter | is_metrics_filter bool metrics_filter | followers_min int metrics_filter | followers_max int metrics_filter | followings_min int metrics_filter | followings_max int metrics_filter | posts_min int metrics_filter | posts_max int metrics_filter | last_posted_min int metrics_filter | last_posted_max int metrics_filter | verified_filter int(0/1/2) filters | is_username_filter bool filters | is_username_filter_match_mode bool filters | is_username_filter_whitelist bool filters | is_gender_by_name_filter bool filters | gender_by_name_choice str filters | is_gender_skip_mode bool filters | is_filter_by_id bool filters | bypass_potential_spam bool filters | potential_spam_flag_checker bool filters | mass_actions_no_stories bool filters | add_users_bm bool bookmarks | add_users_bm_days int bookmarks | language_detection_notices bool language | language_web_data bool language | language_skip_accounts bool language | reposts_enable bool reposts | reposts_action str reposts | reposts_min int reposts | reposts_max int reposts | reposts_speed int reposts | reposts_max_per_day int reposts | cf_enable bool close_friends | cf_followers bool close_friends | cf_action str close_friends | cf_number int close_friends | cf_speed int close_friends | cf_skip_suggested bool close_friends | cf_infinite bool close_friends | cf_new_followers bool close_friends | is_telegram_analytics bool notifications | is_telegram_errors bool notifications | tg_chat_id int notifications | delay_telegram int notifications | custom_proxy str proxy | custom_proxy_data_scrapping bool proxy | mqtt bool advanced | async_masslooking bool advanced | async_masslooking_batch int advanced | async_story_likes bool advanced | async_story_likes_batch int advanced | debug_mode bool advanced | keywords_cleaner bool advanced | bypass_challenges_automatically bool advanced | use_get_likers bool advanced | dfs_enable bool advanced | source_tracking bool advanced | track_only_new_followers bool advanced | target_quality_tracking bool advanced | mc_target_linking_interval int mother_child | mc_interaction_frequency int mother_child | activity_time bool activity_time | activity_time_randomize bool activity_time | activity_time_min_hd str activity_time | activity_time_max_hd str activity_time | activity_time_min_h str activity_time | activity_time_max_h str activity_time ### GET /api/v1/reactions/logs Query: page, per_page Returns list: id, account_id, account_username, status, data (object), date ### GET /api/v1/reactions/logs/{account_id} Logs for one account. ### GET /api/v1/reactions/stats Reaction stats rows for all accounts. ### GET /api/v1/reactions/stats/{account_id} Stats for one account. ## Repost Pro Endpoints Requires: Repost Pro plugin installed AND user plan includes "repost-pro" module. 503 if plugin not installed. 403 if plan doesn't include module. account_id in path = np_accounts.id (not Instagram numeric ID). Schedule must be created first through the web panel (/e/repost-pro/{account_id}). API manages existing schedules only. Tables: np_repost_pro_schedule, np_repost_pro_log. ### GET /api/v1/repost/schedules Returns list of all repost schedules. Fields: id, account_id, account_username, instagram_id, status, is_active, speed, remove_delay, daily_pause, daily_pause_from, daily_pause_to, caption, first_comment, metadata_location, metadata_user, collections_shuffle, target (array of {type,id,value,link}), data (object), schedule_date, end_date, last_action_date Meta: count (int) ### GET /api/v1/repost/schedules/{account_id} Single schedule for one account. 404 if not owned. ### POST /api/v1/repost/schedules/bulk/activate Activate all (or subset) of user's repost schedules. Sets is_active=1. Body (optional): {"account_ids":[5,10,15]} — omit to affect all schedules. Unknown/unowned IDs silently skipped, reported in "skipped" array. Returns: {"data":{"affected":N,"account_ids":[...],"skipped":[...]}} 404 if no valid schedules found. ### POST /api/v1/repost/schedules/bulk/pause Pause all (or subset) of user's repost schedules. Sets is_active=0. Same body/response shape as bulk/activate. ### POST /api/v1/repost/schedules/{account_id}/activate Starts reposting for one account. 404 if no schedule exists (create via web panel). 409 if already active. Returns: {"data":{"account_id":N,"is_active":true}} ### POST /api/v1/repost/schedules/{account_id}/pause Pauses reposting for one account. 409 if already paused. Returns: {"data":{"account_id":N,"is_active":false}} ### GET /api/v1/repost/targets/types Returns catalog of all valid target types. Returns: {"data":{"types":[{"type":"hashtag","description":"...","requires_id":false},...], "total":13}} Valid types and requires_id: hashtag (false) — posts under a hashtag hashtag_reels (false) — reels under a hashtag location (true) — posts at a specific Instagram location (id = numeric location ID) people (false) — posts from a specific Instagram user people_reels (false) — reels from a specific Instagram user music (true) — reels using a specific music track (id = Instagram music track ID) collection (true) — saved Instagram collection (id = Instagram collection ID) instagram_post (false) — one specific Instagram post by URL (value = full post URL) tiktok_hashtag (false) — TikTok posts under a hashtag tiktok_user (false) — TikTok posts from a specific user tiktok_keyword (false) — TikTok posts matching a keyword search tiktok_trending (false) — TikTok trending content by region (value = region code, e.g. "US") tiktok_post (false) — one specific TikTok video by URL ### GET /api/v1/repost/schedules/{account_id}/targets Returns targets configured for this schedule. Response: {"data":{"targets":[{"type":"hashtag","id":"nature","value":"nature","link":""},...], "total":N}} ### POST /api/v1/repost/schedules/{account_id}/targets Add a target source to a schedule. 404 if schedule not found. Body: {"type":"str (required)", "value":"str (required)", "id":"str (required if requires_id=true)", "link":"str (optional, for instagram_post/tiktok_post)"} For requires_id=false types, id is auto-set equal to value if omitted. For instagram_post / tiktok_post: value can be the post URL directly; link is auto-set from value. 409 if a target with same type+value already exists. Returns 201 with new target object and updated total. ### DELETE /api/v1/repost/schedules/{account_id}/targets Remove one or all targets of a given type. Body: {"type":"str (required)", "value":"str (optional — omit to remove ALL targets of this type)"} Returns: {"data":{"removed":N,"total":N}} ### GET /api/v1/repost/schedules/{account_id}/settings Returns all settings grouped by category: schedule, caption, media_types, targeting. ### PATCH /api/v1/repost/schedules/{account_id}/settings Partial update — only sent fields are changed. 422 if unknown or read-only field sent (is_active is read-only here — use /activate or /pause). Returns: {"data":{"account_id":N,"updated_fields":[...]}} Writable settings fields (field → type → category): speed str(very_slow|slow|medium|fast|very_fast) schedule | remove_delay int(seconds,0=never) schedule | daily_pause bool schedule | daily_pause_from str(HH:MM) schedule | daily_pause_to str(HH:MM) schedule | metadata_location bool schedule | metadata_user bool schedule | collections_shuffle bool schedule | caption str caption | first_comment str caption | photo_posts bool media_types | video_posts bool media_types | album_posts bool media_types | reels_posts bool media_types | targets_shuffle bool targeting | custom_repost_time str(repost[HH:MM] pattern) targeting Field notes: - speed: uses admin-configured per-tier posts/day values (very_slow < slow < medium < fast < very_fast) - remove_delay: 0=never, 900=15min, 1800=30min, 3600=1h, etc. up to 604800 (7 days) - daily_pause: pauses all reposts daily between daily_pause_from and daily_pause_to (UTC) - metadata_location: copies original post's location tag to the repost - metadata_user: tags original content creator in the reposted item - collections_shuffle: when target is a saved Collection, shuffles media order instead of sequential - caption: supports spintax {a|b|c} and {{original_caption}} placeholder - first_comment: posted immediately after repost; supports spintax; empty = skip - photo_posts/video_posts/album_posts/reels_posts: filter media types; all false = accept all types - targets_shuffle: randomises feed item selection instead of always taking most recent - custom_repost_time: overrides speed with exact daily times, e.g. "repost[10:00],repost[18:00]"; empty = use speed ### GET /api/v1/repost/logs Query: page, per_page Returns list: id, account_id, account_username, status, original_media_code, data (object), date, is_removable, remove_scheduled, is_deleted, remove_date, status_share_to_stories, status_first_comment, status_caption Meta: page, per_page, total, total_pages ### GET /api/v1/repost/logs/{account_id} Logs for one account. Same fields and pagination as above. Log field notes: - status: success | error | skipped - original_media_code: Instagram shortcode of the source post (the /p/XYZ part of the post URL) - is_removable: true = this repost is scheduled for auto-deletion after remove_delay - remove_scheduled: datetime when auto-delete is scheduled to run - is_deleted: true once auto-delete has been carried out - status_share_to_stories / status_first_comment / status_caption: sub-action results ## Admin Endpoints Require account_type IN ('admin','developer'). 403 for regular users. ### GET /api/v1/admin/keys Query: user_id (int), is_active (0|1), page, per_page Returns all API keys across all users. ### POST /api/v1/admin/keys/{id}/revoke Revokes any key by ID. ### GET /api/v1/admin/logs Query: user_id, api_key_id, method, status_code, status_class (4xx|5xx), page, per_page Returns all request logs. ### GET /api/v1/admin/stats Returns: total_requests, top_endpoints (array), top_users (array by request count) ### GET /api/v1/admin/users Query: search (str), is_active (0|1), page, per_page Returns all users with API key counts. ## Key Workflows ### Fresh Instagram Login (no challenge) POST /api/v1/accounts/login → 201 with account object ### Instagram Login with Email Challenge (manual code) 1. POST /api/v1/accounts/login → 200 with challenge_token + challenge_type:"email" 2. User reads code from email inbox 3. POST /api/v1/accounts/challenge {challenge_token, code} → 201 ### Instagram Login with Email Challenge (IMAP auto-fetch) 1. POST /api/v1/accounts/login with imap_server+imap_username+imap_password → 200 with challenge_token 2. POST /api/v1/accounts/challenge {challenge_token, auto_imap:true} → 201 if code found, 202 {retry:true} if email not yet arrived (retry same request) ### Instagram Login with 2FA (TOTP) POST /api/v1/accounts/login with twofa_secret → auto-resolved → 201 ### Re-login (account flagged login_required=1) POST /api/v1/accounts/login with same username + (new) password. Condition: account must exist in np_accounts with login_required=1. If login_required=0 → 409. Result: np_accounts row is UPDATED in-place (password, session, login_required→0, last_login). HTTP 200. Account-limit check is skipped (existing slot reused). Challenge flow works identically — challenge_token returned if Instagram requires verification. On challenge completion: np_accounts row updated (not inserted). HTTP 200. ### Activate Reactions for an account POST /api/v1/reactions/schedules/{account_id}/activate ### Update Reactions settings PATCH /api/v1/reactions/schedules/{account_id}/settings with only changed fields ### Activate Repost Pro for an account POST /api/v1/repost/schedules/{account_id}/activate ### Update Repost Pro settings PATCH /api/v1/repost/schedules/{account_id}/settings with only changed fields ### Add a repost target and activate 1. POST /api/v1/repost/schedules/{account_id}/targets {"type":"hashtag","value":"nature"} 2. POST /api/v1/repost/schedules/{account_id}/activate ### Bulk pause all repost schedules POST /api/v1/repost/schedules/bulk/pause (no body) ## Data Models ### Account object (returned from login/challenge/accounts endpoints) {id, user_id, instagram_id, username, proxy, mobile_proxy, login_required (bool), slave (bool), child (bool), rpa (bool), engagement_mod (bool), counter (int), last_login, date} Note: password is never returned. imap credentials stored in data JSON column, never returned via API. ### Post object {id, user_id, account_id, status (saved|scheduled|published|failed), type, caption, first_comment, location, link, media_ids (comma-sep), is_scheduled (bool), schedule_date, publish_date, create_date} ### Challenge record (internal, np_api_login_challenges) {token (64hex), user_id, username, password_enc (Defuse), proxy, api_path, twofa_secret, imap_server, imap_port (int), imap_username, imap_password_enc (Defuse), challenge_at (unix int), created_at, expires_at} TTL: 600 seconds from creation or last update. ### API Key object {id, user_id, key_hash (SHA-256, never exposed), key_prefix (first 12 chars), name, is_active, request_count, last_used_at, last_used_ip, last_used_ua, created_at, revoked_at} Raw key format: sht_<64 random hex chars>. Shown once at generation. Stored as SHA-256. ## Post Actions API Endpoints Module gate: "post-actions-api" must be in user's package modules[]. Plugin active check: np_post_actions_schedule table must exist. Actions: like | comment | follow ### Endpoints GET /api/v1/post-actions/actions — list allowed action types with field requirements POST /api/v1/post-actions/jobs — schedule a new action job GET /api/v1/post-actions/jobs — list user's jobs (paginated). Filter: ?status=pending|processing|success|partial|failed|cancelled GET /api/v1/post-actions/jobs/{id} — get single job DELETE /api/v1/post-actions/jobs/{id} — cancel a pending job (409 if not pending) GET /api/v1/post-actions/logs — list run logs (paginated). Filter: ?post_url=&action= GET /api/v1/post-actions/logs/{job_id} — run logs for a specific job GET /api/v1/post-actions/usage — per-account usage records. Filter: ?post_url=&action= ### POST /api/v1/post-actions/jobs — request body action string required "like" | "comment" | "follow" post_url string required* Instagram URL (/p/ /reel/ /tv/). For follow: also accepts profile URL. *Not required if target_username set. target_username string optional Plain username for follow action. Alternative to post_url. count int optional Number of accounts to use. Default 10. Max 10000. comment_text string required Required when action="comment". The comment text. URL normalisation: /p/, /reel/, /reels/, /tv/ → canonical https://www.instagram.com/{type}/{code}/ For follow: accepts post URL (follows post owner), profile URL, or plain username via target_username. ### Job status flow pending → processing → success | partial | failed pending → cancelled (via DELETE) Jobs stuck in "processing" for >30 min are auto-reset to "pending" by next cron run. ### Job object fields id, post_url, action, count, comment_text, status, scheduled_at, started_at, finished_at, created_at, updated_at ### Run log fields id, schedule_id, post_url, action, requested_count, performed_count, status (success|partial|failed), message, details.results[] (per-account: account_id, username, success, via, error), started_at, finished_at, created_at ### Usage record fields id, account_id, post_url, action (from action_type column), media_id, created_at Unique constraint: (post_url, action_type, account_id) — each account used at most once per post+action globally. ### Key Workflows 1. Like a post with 10 accounts: POST /post-actions/jobs {"action":"like","post_url":"https://www.instagram.com/p/CODE/","count":10} → poll GET /post-actions/jobs/{id} until status != "pending"/"processing" → GET /post-actions/logs/{id} for per-account details 2. Comment on a post: POST /post-actions/jobs {"action":"comment","post_url":"...","count":5,"comment_text":"Nice post!"} 3. Follow from username: POST /post-actions/jobs {"action":"follow","target_username":"johndoe","count":20} 4. Check which accounts already acted on a post: GET /post-actions/usage?post_url=https%3A%2F%2Fwww.instagram.com%2Fp%2FCODE%2F&action=like ## Leads Finder Endpoints Module gate: "parser" must be in user's package modules[]. Plugin active check: np_parser_schedule table must exist. ### Endpoints GET /api/v1/leads/schedules — list all user's leads schedules (paginated) GET /api/v1/leads/schedules/{id} — get single schedule by ID POST /api/v1/leads/schedules/{id}/start — activate schedule (is_active=1, schedule_date=now) POST /api/v1/leads/schedules/{id}/stop — stop schedule (is_active=0, is_running=0) GET /api/v1/leads/schedules/{id}/export — paginated leads export from txt file. Params: ?page=&per_page= (default 1000, max 5000) GET /api/v1/leads/logs — list run logs (paginated). Filter: ?account_id=&status= GET /api/v1/leads/logs/{id} — get single log entry GET /api/v1/leads/stats — list parse stats (paginated). Filter: ?account_id=&type= ### Schedule object fields id, account_id, task_name, status (running|active|scheduled|inactive), is_active, is_running, targets (decoded JSON array), target_count, speed, estimated_speed, estimated_speed_parsed, analyzed_percentage, parser_data_type (e.g. "username"), data_fields{nickname, user_id, user_link, full_name, is_business, business_category, city, link, profile_bio, followers_count, is_verified, home_country, whatsapp_number, is_new_to_instagram, is_new_to_instagram_30d} (15 bools), filters{followers_min, followers_max, followings_min, followings_max, posts_min, posts_max, last_posted_min, last_posted_max} (0 = no limit), language_filter{languages (array), language_detection_notices (bool)}, telegram{analytics_enabled, errors_enabled (bools), chat_id, delay (int seconds)}, accelerators{enabled (bool), accounts (array), stop_if_all_disconnected (bool), get_followers_limit, get_following_limit, get_hashtag_limit, get_location_limit, get_liker_limit, get_info_limit (all int)}, custom_delays{enabled (bool), get_followers, get_following, get_hashtag, get_location, get_info, get_likers (all int seconds)}, schedule_date, end_date, last_action_date ### Status values running — is_running=1 (cron currently executing) active — is_active=1, schedule_date in the future scheduled — is_active=1, schedule_date in the past (awaiting next trigger) inactive — is_active=0 ### Target types (14) people_followers, people_likers, people_following, people_followers_following, people_followers_followers, people_following_followers, people_following_following, hashtag, hashtag_likers, location, location_likers, explore, explore_likers, user_id_list ### Export notes File stored at: PLUGINS_PATH/parser/generated-lists/{user_id}/data-parsed-by-{username}.txt If data.task_name is set and the named file exists, that file is used instead. Returns 404 with hint message if no file found (parser has not run yet). Pagination: page (default 1), per_page (default 1000, max 5000). total = total line count. Each item in data.items[] is one username (plain string). ### Log object fields id, account_id, status, message (from data.error.msg or null), details (full decoded data JSON), date ### Stat object fields id, account_id, target, type, parsed_count, date ## AI-DM Endpoints Module gate: "ai-dm" must be in user's package modules[]. Plugin active check: np_ai_dm_schedule table must exist. ### Endpoints GET /api/v1/ai-dm/schedules — list all user's AI-DM schedules (paginated) GET /api/v1/ai-dm/schedules/{id} — get single schedule by ID POST /api/v1/ai-dm/schedules/{id}/activate — activate schedule (is_active=1, schedule_date=now) POST /api/v1/ai-dm/schedules/{id}/pause — pause schedule (is_active=0) GET /api/v1/ai-dm/logs — list run logs (paginated). Filter: ?account_id=&type=&status= GET /api/v1/ai-dm/logs/{id} — get single log entry GET /api/v1/ai-dm/threads — list conversation threads (paginated). Filter: ?account_id= GET /api/v1/ai-dm/threads/{id} — get single thread GET /api/v1/ai-dm/threads/{id}/messages — list messages in thread (paginated, ASC timestamp order) ### Schedule object fields id, account_id, account_name, status (running|active|inactive), is_active (bool), daily_pause{enabled (bool), from (time string), to (time string)}, cta{platform, username, spintax}, settings{ system_prompt, api_key_configured (bool — key itself never returned), model (e.g. "gpt-3.5-turbo" or "gpt-4"), folder (DM inbox folder ID string, "0" = primary), max_per_conversation, max_per_day_requests, max_per_run_requests, max_per_day_messages, max_per_run_messages, max_time (all int or null) }, schedule_date, last_action_date ### Status values running — process_id > 0 (cron process active) active — is_active=1, no running process inactive — is_active=0 ### Thread object fields id, account_id, thread_v2_id, them_user_pk, them_username, them_fullname, item_count, last_activity_at, last_seen_by_us, last_seen_by_them, last_permanent_item_at, last_non_sender_item_at, last_checked ### Message object fields id, thread_id, item_id, timestamp (Instagram microsecond string), item_type (text|like|media_share|…), is_sent_by_us (bool), content (decoded item_content JSON) ### Log object fields (AI-DM) id, account_id, status, type (messages_sent|requests_allowed|…), message (from data.error.msg or null), details (full decoded data JSON), date ## Welcome DM Endpoints Module gate: "welcomedm" must be in user's package modules[]. Plugin active check: np_welcomedm_schedule table must exist. ### Endpoints GET /api/v1/welcomedm/schedules — list all user's Welcome DM schedules (paginated) GET /api/v1/welcomedm/schedules/{id} — get single schedule by ID POST /api/v1/welcomedm/schedules/{id}/activate — activate schedule (is_active=1, schedule_date=now) POST /api/v1/welcomedm/schedules/{id}/pause — pause schedule (is_active=0) GET /api/v1/welcomedm/logs — list run logs (paginated). Filter: ?account_id=&status= GET /api/v1/welcomedm/logs/{id} — get single log entry ### Schedule object fields id, account_id, is_active (bool), status (active|inactive), messages (decoded JSON array of message variant objects), message_count (int), speed ("auto" string or int 1–5), daily_pause{enabled (bool), from (time string), to (time string)}, settings{ skip_opened (bool), mute_thread (bool), has_attachment (bool — file path not exposed), debug_mode (bool) }, schedule_date, end_date, last_action_date ### Log object fields (Welcome DM) id, account_id, status, follower_id (Instagram user PK of new follower or null), message (from data.error.msg or null), details (full decoded data JSON), date ## Security Constraints - API keys stored as SHA-256 (irreversible after generation) - Passwords stored Defuse-encrypted (CRYPTO_KEY from config) - IMAP passwords stored Defuse-encrypted in challenge table and account data column - Ownership enforced server-side on all resource endpoints (user can only access their own data) - Admin endpoints gated on account_type IN ('admin','developer') - Reactions endpoints gated on plugin installation + per-user module entitlement ("reactions") - Repost Pro endpoints gated on plugin installation + per-user module entitlement ("repost-pro") - Post Actions endpoints gated on plugin installation + per-user module entitlement ("post-actions-api") - Leads Finder endpoints gated on plugin installation + per-user module entitlement ("parser") - AI-DM endpoints gated on plugin installation + per-user module entitlement ("ai-dm"); OpenAI api_key never returned - Welcome DM endpoints gated on plugin installation + per-user module entitlement ("welcomedm"); attachment file path never returned - Raw API key format prefix "sht_" allows easy identification and rotation ## Web Panels /e/rest-api/ — user panel: key management, usage stats, endpoint reference (all authenticated users) /e/rest-api/admin — admin panel: all keys, request logs, monitoring (admin/developer only) ## Database Tables (prefix np_) np_api_keys — API key store (key_hash, key_prefix, user_id, is_active, request_count, last_used_*) np_api_logs — per-request log (api_key_id, user_id, method, endpoint, status_code, response_time_ms, ip_address, user_agent, created_at) np_api_login_challenges — pending challenge state (token, credentials enc, IMAP enc, challenge_at, expires_at) np_repost_pro_schedule — repost schedule per account (status, is_active, speed, targets JSON, caption, remove_delay, data JSON) np_repost_pro_log — repost action log (status, original_media_code, data JSON, is_removable, remove_scheduled, is_deleted, sub-action statuses) np_post_actions_schedule — queued action jobs (user_id, post_url, action, comment_text, count, status, scheduled_at, started_at, finished_at) np_post_actions_run_logs — per-job execution log (schedule_id, requested_count, performed_count, status, message, details JSON with per-account results) np_post_actions_usage — per-account usage tracker (post_url, action_type, account_id — unique constraint prevents re-use) np_parser_schedule — leads finder schedule per account (user_id, account_id, target JSON, speed, is_active, is_running, is_executed, data LONGTEXT JSON, process_id, schedule_date, end_date, last_action_date) np_parser_log — leads finder run log (user_id, account_id, status, data TEXT JSON, date) np_parser_stats — leads finder parse stats (user_id, account_id, target, type, parsed_count, date, data TEXT JSON) np_ai_dm_schedule — AI-DM schedule (user_id, account_id, account_name, is_active, process_id, cta_platform, cta_username, cta_spintax, daily_pause, daily_pause_from, daily_pause_to, data LONGTEXT JSON, schedule_date, last_action_date) np_ai_dm_log — AI-DM run log (user_id, account_id, status, type, data TEXT JSON, date) np_ai_dm_threads — AI-DM conversation thread cache (user_id, account_id, thread_v2_id, them_user_pk, them_username, them_fullname, item_count, last_activity_at, last_seen_by_us, last_seen_by_them, last_permanent_item_at, last_non_sender_item_at, last_checked) np_ai_dm_messages — AI-DM message cache (user_id, thread_id, item_id, timestamp, item_type, is_sent_by_us, item_content JSON) np_welcomedm_schedule — Welcome DM schedule per account (user_id, account_id, is_active, messages JSON, speed, daily_pause, daily_pause_from, daily_pause_to, data TEXT JSON, schedule_date, end_date, last_action_date) np_welcomedm_log — Welcome DM run log (user_id, account_id, status, follower_id, data TEXT JSON, date) np_accounts — Instagram accounts (username, password enc, proxy, instagram_id, login_required, data JSON) np_posts — scheduled/published posts np_captions — caption templates np_proxies — shared proxy pool (no user_id) np_wallet_transactions — wallet ledger ## Plugin System Notes The REST API is implemented as a plugin at inc/plugins/rest-api/. Plugin ID: rest-api Plugin namespace: Plugins\RestApi All API routes are registered in route_maps() via Event::bind("router.map"). The np_api_login_challenges table is lazy-created with ALTER TABLE IF NOT EXISTS guards on every request to support existing installs that were deployed before IMAP columns were added. ImapOAuth class is globally autoloaded by the framework (same class used by PMPAccountController::imapGetEmailCode). ## Changelog 1.9.0 2026-03-15 — AI-DM endpoints (schedules CRUD, activate/pause, logs, threads, messages) + Welcome DM endpoints (schedules CRUD, activate/pause, logs) 1.8.0 2026-03-15 — Leads Finder endpoints (schedules CRUD, start/stop, export, logs, stats) 1.7.0 2026-03-15 — Post Actions API endpoints (jobs CRUD, run logs, usage records; actions: like/comment/follow) 1.6.0 2026-03-15 — Repost Pro endpoints (schedules CRUD, targets CRUD, settings PATCH, logs, bulk activate/pause; GET /repost/targets/types) 1.5.0 2026-03-14 — Targets CRUD endpoints (GET|POST|DELETE /reactions/schedules/{id}/targets, GET /reactions/targets/types) 1.4.0 2026-03-13 — Reactions Pro bulk/activate, bulk/pause, bulk/stop endpoints 1.3.0 2026-03-13 — Re-login support: /accounts/login updates existing login_required=1 accounts (HTTP 200, skips account-limit check) 1.2.0 2026-03-13 — IMAP auto-code-fetch for email challenge flow (imap_server/port/username/password on /accounts/login; auto_imap:true on /accounts/challenge; 202 retry pattern) 1.1.0 2026-03-13 — Reactions Pro endpoints (schedules, settings PATCH, logs, stats) 1.0.0 2026-03-13 — Initial release (auth, dashboard, user, accounts, posts, statistics, packages, proxies, captions, wallet, keys, admin)