Skip to content
Splashify Pro
Docs

Instagram Automation

The instagram command (alias: ig) mirrors the /instagram-automation page in the app. From the terminal you can connect your Instagram Login account, list the posts on it, build comment-to-DM automation rules, send DMs in existing conversations, sync historical chats from Meta, and read the automation activity log.

Backed by /api/v1/app/instagram/*. Every call uses your stored oc_live_ token and acts as you — only the account owner can manage Instagram on a team account.

Prerequisites. Your account must have the Instagram Automation feature enabled (the backend gates this with EnsureFeatureEnabled). If your plan doesn't include it, the CLI surfaces the standard plan_required upgrade prompt with the exact upgrade URL.

Quick start

bash
# 1. Get the OAuth URL and open it in your browser
splashify instagram oauth-url

# 2. After the IG redirect, copy the `code` query parameter from the URL
splashify instagram connect --code <code>

# 3. Confirm
splashify instagram                       # show the connected account
splashify instagram media --limit 25      # see what you can target with rules

# 4. Build a rule
splashify instagram rule create \
  --media-id 17841401234567890 \
  --keyword "shipping" \
  --dm-message "Hey! Free shipping today only — code SHIP10 ✨" \
  --comment-reply "Sent you a DM 💌" \
  --active true

# 5. Watch it fire
splashify instagram logs --limit 20

How the connect flow works

The handshake is a three-leg dance and the CLI handles the server-side leg only — the in-browser step still needs a real browser because Instagram Login requires the user to authenticate on instagram.com:

  1. splashify instagram oauth-url returns an authorize_url you open in a browser. The URL embeds your user_id as state so the redirect attributes the connection to the right account.
  2. Instagram redirects to https://app.splashifypro.com/instagram/callback with ?code=...&state=... in the query string. Copy the code value.
  3. splashify instagram connect --code <code> exchanges the code for a long-lived (~60 day) access token server-side, subscribes our app to webhooks for comments + messages, and stamps the row in app_ig_accounts.

After step 3, splashify instagram returns the full account record. Use splashify instagram disconnect to undo (drops every rule and log too).

Command reference

splashify instagram — show the connected account

bash
splashify instagram                     # default
splashify instagram account             # alias
splashify instagram info                # alias

Returns {success, account} where account is null when nothing's connected, or:

json
{
  "ig_user_id":             "17841401234567890",
  "ig_username":            "your.handle",
  "ig_name":                "Your Brand",
  "ig_profile_pic":         "https://…",
  "account_type":           "BUSINESS",
  "connected_at":           "2026-04-12T10:23:04Z",
  "webhooks_subscribed_at": "2026-04-12T10:23:07Z",
  "token_expires_at":       "2026-06-11T10:23:04Z"
}

splashify instagram oauth-url

bash
splashify instagram oauth-url
splashify instagram authorize-url        # alias

Returns the URL to open in a browser plus the redirect_uri the backend expects to handle the callback (https://app.splashifypro.com/instagram/callback in production).

splashify instagram connect --code <code>

Completes the OAuth handshake on the backend.

bash
splashify instagram connect --code AQB0Lg…JaYHFq8KQ

Effects:

  • Exchanges the code for a short-lived token, then upgrades to a long-lived (~60 day) token.
  • Reads /me for username, profile pic, account type.
  • Persists the encrypted token in app_ig_accounts.
  • Subscribes the app to webhooks for comments + messages events on this IG user (best-effort — surfaces in webhooks_subscribed_at).

splashify instagram disconnect

bash
splashify instagram disconnect

Unsubscribes webhooks, deletes the account row, and removes every rule + log entry for this user. There is no undo. To reconnect later, run the whole OAuth dance again — a fresh long-lived token is minted and webhooks re-subscribed.

splashify instagram media [--limit N]

bash
splashify instagram media                # default limit: 50
splashify instagram media --limit 25

Proxies Meta's /me/media listing. Each item has id, caption, media_type, thumbnail_url, permalink, timestamp — exactly the fields you pass to rule create as --media-id, --media-caption, --media-thumbnail, --media-permalink.

splashify instagram logs [--limit N]

bash
splashify instagram logs                  # newest 50
splashify instagram logs --limit 200

The activity feed — every comment-to-DM trigger gets a row here (matched keyword, sender username, rule id, status, error message if Meta refused). Use this to debug why a rule didn't fire.

splashify instagram window --conversation <id>

bash
splashify instagram window --conversation 1c5d2f5e-…

Meta enforces a strict reply window for Instagram DMs:

  • 0–24h since the user last messaged you — free-text DMs allowed.
  • 24h–7d — only HUMAN_AGENT tagged messages (the backend sets this automatically — you don't pass it).
  • >7d — no outbound DMs at all until the user messages you again.

The composer in /messages calls this before every send. From the CLI, use it as a pre-check so a dm won't fail with HTTP 403.

Response shape:

json
{
  "success": true,
  "window": {
    "can_reply":             true,
    "needs_human_agent":     false,
    "reply_window_ends_at":  "2026-05-21T14:00:00Z",
    "last_inbound_at":       "2026-05-20T14:00:00Z"
  }
}

splashify instagram sync

bash
splashify instagram sync

Pulls historical Instagram conversations from Meta and back-fills them into /messages. Useful right after connect so you have something to reply to. Returns a {messages_synced, conversations_synced} summary.

splashify instagram dm — send a DM

bash
# Text only
splashify instagram dm --conversation <conversation_id> --message "Hey!"

# Media only
splashify instagram dm --conversation <conversation_id> \
  --media-url https://cdn.example.com/img.jpg --media-type image

# Both — IG renders them as two bubbles
splashify instagram dm --conversation <conversation_id> \
  --message "Here's the file you asked for ↓" \
  --media-url https://cdn.example.com/brochure.pdf --media-type image

Validation:

  • Refuses with HTTP 403 if the 7-day window has expired (splashify instagram window is the pre-check).
  • Refuses with HTTP 400 if the conversation isn't an Instagram channel (i.e. it's a WhatsApp conv).
  • Refuses with HTTP 502 if Meta refuses the upload (file too large, unsupported format, URL unreachable) — the CLI surfaces Meta's exact error code (subcode 2018007 is the most common one).

Two bubbles. When you pass --message AND --media-url, IG's /me/messages API sends them as two separate messages. The CLI inserts two rows in app_messages matching that — each with its own wa_message_id so the echo webhook can dedup correctly.

Comment-to-DM rules

The rule / rules commands manage automation rules — when a comment on a chosen IG post contains a trigger_keyword, the system sends the commenter a DM (and optionally a public reply on their comment).

Anatomy of a rule

List rules

bash
splashify instagram rules                   # full list, newest first
splashify instagram rules list              # alias

Response shape per rule:

json
{
  "rule_id":         "uuid",
  "media_id":        "17841401234567890",
  "media_caption":   "Spring sale — link in bio",
  "media_thumbnail": "https://…",
  "media_permalink": "https://www.instagram.com/p/…",
  "trigger_keyword": "shipping",
  "dm_message":      "Hey! Free shipping today only…",
  "dm_media_url":    "",
  "dm_media_type":   "",
  "comment_reply":   "Sent you a DM 💌",
  "is_active":       true,
  "triggered_count": 12,
  "created_at":      "2026-04-12T11:00:00Z",
  "updated_at":      "2026-04-12T11:00:00Z"
}

Show one rule

bash
splashify instagram rule <rule_id>

The CLI fetches the list and filters client-side (no per-id GET on the backend).

Create a rule

bash
splashify instagram rule create \
  --media-id <media_id_from_media_list> \
  --keyword "buy" \
  --dm-message "Here's the link — https://shop.example.com/?utm=ig" \
  --comment-reply "DM sent 💌" \
  --active true

Optional flags:

bash
splashify instagram rule create \
  --media-id <id> --keyword "free" --dm-message "Use code FREE15 …" \
  --dm-media-url https://cdn.example.com/coupon.jpg --dm-media-type image \
  --media-caption "Spring drop — comment FREE for code"

Update a rule

bash
splashify instagram rule update <rule_id> --keyword "new-keyword"
splashify instagram rule update <rule_id> --dm-message "Updated body" --active false
splashify instagram rule update <rule_id> --dm-media-url "" --dm-media-type ""   # clear the media

Read-modify-write. The backend's PATCH writes every editable column on every call (trigger_keyword, dm_message, dm_media_url, dm_media_type, comment_reply, is_active), so a partial flag set would blank the others. The CLI loads the rule first, overlays only your explicitly-passed flags, then submits the full body — fields you didn't mention stay intact. Same pattern as splashify canned update, waba update, opt ….

Toggle activate / deactivate

bash
splashify instagram rule toggle <rule_id>

There is no dedicated toggle endpoint for IG rules (unlike canned messages), so the CLI implements this as load → flip is_active → PATCH. The output is the standard PATCH response.

Delete a rule

bash
splashify instagram rule delete <rule_id>
splashify instagram rule rm     <rule_id>      # alias

Permanent. The row is also removed from the by-media index so the rule won't fire on incoming comments.

End-to-end workflows

Bootstrap automation from scratch

bash
# 1. Connect
splashify instagram oauth-url
# (open the URL, paste the code from the redirect)
splashify instagram connect --code AQB…

# 2. Discover targets
splashify instagram media --limit 50 | jq '.media[] | {id, caption: (.caption // "")[:80], permalink}'

# 3. Create one rule per target post (e.g. from a CSV)
#    csv format: media_id,keyword,dm,comment_reply
while IFS=, read -r mid kw dm reply; do
  splashify instagram rule create \
    --media-id "$mid" --keyword "$kw" --dm-message "$dm" --comment-reply "$reply" --active true
done < ig-rules.csv

# 4. Verify
splashify instagram rules | jq '.rules[] | {rule_id, keyword: .trigger_keyword, active: .is_active}'

Deactivate every rule on a specific post

bash
TARGET_MEDIA=17841401234567890
splashify instagram rules | \
  jq -r --arg m "$TARGET_MEDIA" '.rules[] | select(.media_id == $m) | .rule_id' | \
  xargs -I{} splashify instagram rule toggle {}

Find the rule that fired most often last week

bash
splashify instagram rules | \
  jq 'first(.rules | sort_by(.triggered_count) | reverse | .[]) |
      {rule_id, keyword: .trigger_keyword, fired: .triggered_count, dm: .dm_message[:60]}'

Replace the DM body on every active rule

bash
NEW_DM="Updated promo — see https://shop.example.com/spring"
splashify instagram rules | \
  jq -r '.rules[] | select(.is_active) | .rule_id' | \
  xargs -I{} splashify instagram rule update {} --dm-message "$NEW_DM"

Reply to an active conversation

bash
# Find an open IG conversation
CONV=$(splashify conversations --status open | \
       jq -r '.conversations[] | select(.channel == "instagram") | .id' | head -1)

# Pre-check the reply window
splashify instagram window --conversation "$CONV" | jq '.window'

# Send
splashify instagram dm --conversation "$CONV" --message "Thanks for the message!"

Re-upload the welcome image + swap it on every rule

bash
NEW_URL=$(splashify media upload ./welcome-v2.jpg | jq -r '.file_url')
splashify instagram rules | jq -r '.rules[] | select(.dm_media_url != "") | .rule_id' | \
  xargs -I{} splashify instagram rule update {} --dm-media-url "$NEW_URL" --dm-media-type image

Troubleshooting

"Instagram is not configured on the server" — the partner backend doesn't have INSTAGRAM_APP_ID / INSTAGRAM_APP_SECRET set in its env. Reseller-only — contact your provider.

splashify instagram returns { "account": null } — you haven't connected an IG account yet. Run the oauth-url → browser → connect flow above.

splashify instagram connect returns "Could not complete Instagram login" — the --code was already redeemed (codes are one-use), or the backend's INSTAGRAM_REDIRECT_URI doesn't match the one configured in the Meta app. Get a fresh code from oauth-url.

splashify instagram rule create returns "media_id, trigger_keyword and dm_message are required" — at least one of those is empty after trimming whitespace. The CLI catches this before the round-trip, but if you only see it from --data / generic api you forgot a required field.

DM send returns "This user hasn't messaged you in over 7 days …" — Meta's 7-day window expired. Wait for the user to message first; there's no override.

DM send returns "Instagram couldn't accept this attachment" — Meta subcode 2018007. The most common causes: image > 8MB, video > 25MB, unsupported format (must be JPG/PNG/MP4/MP3/WAV), or the URL is unreachable from Meta's servers. Test the URL with curl -I first.

Rule fires but no DM arrives — check splashify instagram logs. If the log row shows status: "skipped" the commenter is your own IG account (self-comments never trigger). If it shows a Meta error code, the access token may have expired — reconnect.

Webhook subscription stuck — the response after connect shows webhooks_subscribed_at: null. The subscribe is best-effort + async; re-run splashify instagram connect --code <code> (with a fresh code) to retry, or wait — every successful send refreshes the subscription.

See also