Skip to content

Instagram music, collaborators and trial reels - #406

Open
paulocastellano wants to merge 90 commits into
mainfrom
instagram-music-collaborators-trial-reels
Open

paulocastellano wants to merge 90 commits into
mainfrom
instagram-music-collaborators-trial-reels

Conversation

@paulocastellano

@paulocastellano paulocastellano commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

  • Instagram: music on reels (accounts connected through Facebook), collaborators on feed, carousel and reels, and trial reels. Available on the web, the REST API and MCP.
  • Reconnecting an Instagram account from Instagram Login to Facebook (or back) keeps the same card and its posts.
  • Composer: asks before losing unsaved changes (close, links, browser Back), remembers the last used channels, and searches ignore accents.
  • Publish page: notes open inside the post card, Add to queue explains itself, and sent posts get Copy link / Share link.
  • Discord: only the channel id is accepted from the request; the name comes from the cached channel list.
  • Other: network-specific API endpoints moved into their own controllers (same URLs); every post's stored meta now goes through PostMeta::forChannel; small changes to the welcome MCP step and the connect Finish button.

Deploy

  • Run php artisan social:check-connections once after deploying.
  • Before deploying, confirm the production Meta app can call the Instagram audio endpoints, and that /me on graph.instagram.com returns user_id for Instagram Login tokens.
  • No migrations, config or env changes.
  • After merging, update the docs (Docs: Instagram music, collaborators, trial reels and people tags (after #406) #411): two new API endpoints and two MCP tools, the new Instagram meta keys, and meta.channel_name no longer being accepted.

Closes #213

Manual test checklist

Ordered by risk. "Automated" marks what tests already cover with faked network calls, so the manual pass is about the real APIs.

High risk: real Meta APIs and identities

  • Daily connection check (php artisan social:check-connections, with Horizon running). If Instagram refused the user_id field, every Instagram Login account would be marked disconnected.
    • Instagram Login: stays Connected, and an account without meta.ig_user_id gets it back from the check (tested on 2026-10-14 by removing the key first). updated_at moves because the check stamps last_verified_at on every account, as on main; writing ig_user_id itself does not move it (automated).
    • Every other network connected locally (Bluesky, Discord, Facebook, Google Business, LinkedIn, Mastodon, Pinterest, Threads, TikTok, X, YouTube) stays Connected.
    • Instagram connected through Facebook stays Connected (castellanospelomundo, verified on 2026-10-14).
  • Publishing on Instagram through Facebook. Check the post's status and error after each:
    • a reel with music and chosen volumes (published on 2026-10-14 with music at 28 and video at 100);
    • a reel with 3 collaborators and music: published (instagram.com/reel/DedRjnNCubA);
    • a trial reel, shown as a trial reel in the Instagram app (published on 2026-10-14 in Manual mode, instagram.com/reel/DedSotuAsq5);
    • a feed image with 2 collaborators: an invite on each collaborator's account;
    • a carousel with 2 collaborators: an invite on each collaborator's account;
    • a story with collaborators set: it publishes and the collaborators are ignored;
    • a collaborator username that does not exist: the post fails with Meta code 110, subcode 2207018 ("Invalid user id"), shown to the user in their language: "The following users can't be accessed: ".
  • Instagram publishing without the new options (both connection types): a feed image with people tags, a carousel, a story, a reel with Share to feed off. Everything publishes as before.
  • Connecting and reconnecting Instagram:
    • connect a new Instagram Login account: meta.ig_user_id is stored;
    • then connect through Facebook in the same workspace: that account is not offered again;
    • reconnect the Instagram Login card through Facebook: same card, now via Facebook, with its scheduled posts still scheduled;
    • reconnect through Facebook while logged into another Instagram account: refused, the card is not changed;
    • reconnect a card connected through Facebook using Instagram Login while it has a scheduled reel with music: the reel later fails with the clear "requires Facebook" message;
    • an Instagram Login account connected before this deploy: run social:check-connections, then repeat the second check.
  • Instagram audio against the production Meta app (web composer, API instagram-audio, MCP search-instagram-audio-tool / get-instagram-audio-tool):
    • trending tracks without a search; a search returns only tracks that name every word;
    • the details of a track return fresh cover and preview links;
    • an Instagram Login account is refused;
    • about 30 different searches in a minute end in "too many requests".

Medium risk: shared flows every network goes through

  • Every network, create and edit through the composer. For each, save, reopen and check that nothing was lost (all stored meta now goes through PostMeta::forChannel):
    • X: a thread with media on a reply
    • LinkedIn: a profile post, a page post, a PDF with a title
    • Facebook: a feed post with the link preview removed, a story
    • Threads: a topic tag
    • Bluesky and Mastodon: a thread, a content warning on Mastodon
    • Pinterest: a board, a title and a link
    • TikTok: privacy level and interaction switches
    • YouTube: a Short, and a video with title, category and privacy
    • Telegram: edit a post that already has reactions; the reactions stay
    • Google Business: a post and an event
    • Discord: pick a channel; the name shows in the preview after reopening
  • Required settings still block scheduling: Pinterest without a board, Discord without a channel, TikTok without privacy, YouTube without a title.
  • Unsaved changes on an edited saved post:
    • X, Escape, a click outside and a sidebar link each ask first; Keep keeps the edit, Discard drops it;
    • browser Back asks; Back, Keep, Back again still asks;
    • Save closes without asking, and Back afterwards goes to the previous page in one click;
    • open Edit from the calendar, a channel's Drafts tab and the Approvals tab: change, save, then Back twice lands where you came from;
    • Cmd+R with unsaved changes shows the browser's own prompt;
    • the workspace switcher and the command palette ask first;
    • click into the text right away on a TikTok, Pinterest, Discord, YouTube and Google Business post, wait for its settings to load, close: nothing is asked;
    • closing while a file is still uploading asks first;
    • untouched posts and new posts never ask.
  • Flows across networks:
    • one post for several networks, "Customize for each network", then back to step 1 (the overrides are dropped);
    • media in the tray and the media editor on an image post and a video post: upload, reorder, edit, remove, retry a failed upload in one network card;
    • queue: Next, Top, and "+ New" on a free slot;
    • a recurring post: the next occurrence keeps its settings (on Discord, its channel name);
    • duplicate a post on each network you tested;
    • approval: a member who needs approval saves a post, an admin approves one and rejects another; also "Add to queue" by that member creates a request instead of queueing;
    • "Create another" in the new post composer saves once and opens a fresh composer.
  • Publish page:
    • notes inside the card: count, add, edit, delete, on a phone too, and in the Drafts, Scheduled, Approvals and Sent tabs (the footer must not wrap badly);
    • Enter sends a note, Shift+Enter adds a line, and the field grows with it;
    • a note link from an email opens the right card's notes and highlights the note;
    • Add to queue: a tooltip with posting times; without them, the popover links to posting times (a member is told to ask an admin);
    • Copy link and Share link on a sent post, also from the calendar popover;
    • Queue tab: drag and Move up/down with notes open and closed.
  • Discord name: after approving a Discord post, or using "Move to drafts" / "Add to queue" on its card, check whether the channel name still shows (see the open question below).
  • Repurpose: activate an existing repurpose to Instagram reels and to Discord; an Instagram Login reel destination with music is refused; process one item and check the created posts.
  • REST API and MCP:
    • the moved endpoints answer as before: Pinterest boards (list and create), TikTok creator info, Discord channels, posting schedule, queue slots; a wrong-network account gets the same 422;
    • create a reel with collaborators, trial_graduation and audio (audio.id as a string of digits);
    • trial with collaborators, music on an Instagram Login account, and a numeric audio.id are each refused;
    • attach media with alt, with meta.alt_text (it wins) and with user_tags;
    • a Discord post with only channel_id shows the name in the app when the channels were listed in the last 5 minutes;
    • a webhook on post.created for a reel with music carries the new meta.

Low risk: visual and moved code

  • Composer on desktop: centred and sharp at 1280×800, 1440×900 and ~1024×700, and the expand toggle still goes full screen.
  • Radio buttons show a centred dot in light and dark: content type rows (Instagram, Facebook), Google Business topic type, the posting goal after connecting, repurpose publish mode, the referral survey.
  • Media editor at ~1024–1280px: profile photo, workspace logo, an Ideas image and a composer image fit the wider sidebar.
  • Video previews play and pause normally on X, TikTok, YouTube and Facebook.
  • Searches (now word by word, ignoring accents): language, time zone (also by offset, e.g. "-03:00"), channel filter, label filter, ideas toolbar and stage picker, Pinterest board picker, the channel insights post type filter.
  • Channel picker: search with accents and several words, Select all with a search, last used channels pre-selected.
  • Connect Finish shows its loading state while saving, on any network.
  • Welcome MCP step: no chat input, every tab on one line, the examples rotate every 3 seconds.
  • New labels fit on one line in French and German.

Decisions

  • Discord channel name: it is read only from the channel list cached in the last 5 minutes, so a later save (approval, card action, API edit) on a cold cache drops a stored name. Accepted: Discord has almost no production use, and new posts from the composer always have the list cached.

Paulo Castellano added 30 commits October 13, 2026 09:49
Reels and feed posts on Instagram get the three options Instagram's
publishing API exposes, with the same composer flow as Buffer:

- Music: search Instagram audio (music and original sounds, with covers
  and hover previews) and attach a track with its music and video volume.
  Only accounts connected through Facebook can publish it; an Instagram
  Login account gets "Save & link", which saves the post as a draft and
  reconnects the same card through Facebook, keeping its posts.
- Collaborators: up to three Instagram usernames on feed posts, carousels
  and reels.
- Trial reels: Auto (SS_PERFORMANCE) or Manual graduation, confirmed each
  time and not combined with collaborators.

Reconnecting across the two Instagram platforms now matches the Instagram
professional account id, since Instagram Login reports an app-scoped id.
Radio dots are painted on the ring itself so they stay centred at
fractional pixel offsets.
- A rejected play of an earlier preview no longer stops the track playing
  now; the preview video ignores the AbortError of its own pause.
- Save & link keeps an edited scheduled or pending post on its schedule and
  only saves a draft as a draft; the connect step opens from the save's own
  success callback instead of the first router visit that finishes.
- instagramAccountId() asks Instagram at most once per account instance.
- A new connection no longer seats an Instagram account already connected
  under the other Instagram platform when the two report different ids.
- An invalid collaborator in any position shows its error.
- Creator pictures are looked up for the first ten original sounds only, and
  a failed lookup is remembered for ten minutes.
…xpired

Meta's cover and preview links are temporary. Publishing only sends the
audio id, so a reel scheduled weeks ahead keeps its music; reopening it in
the composer now shows the music icon instead of a broken image and hides
the play button when the preview can no longer load.
…nd MCP parity

Music and trial reels only exist on reels and the publisher never sends them
elsewhere, so a feed post or story carrying them is no longer refused; the
rules take the post's content type on every web, API, MCP and repurpose path.
The full parity suite now publishes a carousel with a collaborator, a trial
reel, and a reel through Facebook with music and volumes from web, API and
MCP alike.
- GET /api/social-accounts/{account}/instagram-audio (q, type) and
  /instagram-audio/{audioId}, plus search-instagram-audio-tool and
  get-instagram-audio-tool, run the same SearchInstagramAudio and
  GetInstagramAudio actions as the composer. Every track carries its type.
- Searches are kept for ten minutes per account, catalogue and query, and
  an audio asset for an hour. A workspace may make 30 uncached calls to the
  Audio API a minute across web, API and MCP; past that the lookup answers
  429 without calling Meta.
- An Instagram account connected without Facebook gets the same "connect
  through Facebook" answer on every surface.
- The composer refreshes a picked track's expired cover and preview from its
  details before falling back to the music icon.
"Texto alternativo" and "Marcar pessoas" ran over each other in the
three-column tab bar of the media editor, as did the alt text, tag people
and thumbnail labels in Russian, Ukrainian, Greek, French, German, Turkish
and Arabic. Every label now fits in ten characters, and the single-line
copy test guards the four tab keys.
The tag list shows @username as a link to the profile, opening in a new
tab, with the same helper the collaborators chips use.
The list shows a count out of 20, one row per person with a numbered badge,
the profile link and quiet Move and remove actions. Move highlights that
tag's marker, asks for a new position on the image and places it there on
the next click; Cancel or starting to tag drops it.
…ser edges

- Closing the composer on an edited post with unsaved changes asks first
  (Cancel, Discard), and so do an Inertia visit away and a tab reload. A new
  post keeps closing at once: its autosave offers it back. The guard is the
  reusable useUnsavedChanges composable and UnsavedChangesDialog.
- A sent post with a network link offers "Share link in a post", opening the
  composer with the link and every workspace channel selected, and "Copy
  link", in the card menu, the calendar popover and the post details.
- The composer is placed on whole pixels instead of translate(-50%, -50%),
  so its header and footer borders stay 1px at odd window sizes.
- The Instagram feed preview no longer shows dark lines above and below an
  empty media area.
The notes button joins the card's action row (icon, "Notes", count) instead
of a popover beside the card on desktop and over the thumbnails on mobile.
It expands the card with the notes and the note composer, using the
shadcn Collapsible and its collapsible-down/up animations, and closes it
again on the next click. PostNotesPopover is removed.
The notes toggle is an icon button again: an empty bubble without notes,
and a filled bubble with the count inside (1 to 99, then 99+) once there
are notes, which reads better than the faint count pill and saves room in
the card's action row.
… badge

The hand-drawn bubble is gone: the toggle shows IconMessageCircle without
notes and IconMessageCircleFilled with them, and the count (1 to 99, then
99+) sits in a primary badge on the button's corner.
The notes toggle reads "Notes" again next to the Tabler message icon, and
its count (1 to 99, then 99+) sits in a primary pill instead of the faint
grey one.
- The note composer is a rounded field like the MCP welcome prompt: the text
  sits inside the pill and the round send button turns primary once there
  is something to send.
- Notes read as a thread: larger avatars, the author in bold beside the
  time, the text below, and a soft hover.
- The notes toggle has a tooltip that says "Show notes" or "Hide notes"
  depending on whether the notes are open, in every locale.
- The note composer is the system's InputGroup with a one-line input and
  the primary send button inside it on the right; Enter sends.
- Notes are full-width rows separated by dividers, with the author, time
  and text aligned, instead of rounded hover boxes.
…w and cover refresh, save and link never publishes
… queue button component, note count from the thread
Paulo Castellano added 6 commits October 13, 2026 19:07
… a save, generic channel search copy in the composer, ig_user_id on empty meta, tighter guard and Discord cache tests
…whose music needs Facebook, restore page state on the held history copy, one discard dialog, and close the round 4 coverage gaps
…ty matching reads, and cover Discord names on recurrence and repurpose, draft music without a track id, the music category and reel-only gating
… and unmount restore, and cover validation failure, undo after back, a post published meanwhile and a reload behind the composer
…param helper for Instagram, and document stored post meta, Instagram reel rules, model HTTP, the unsaved-changes guard and browser test traps
@paulocastellano paulocastellano changed the title Instagram: music, collaborators and trial reels Instagram music, collaborators and trial reels Oct 14, 2026
Paulo Castellano added 20 commits October 13, 2026 21:20
…line, and rotate the examples every 3 seconds
…lti-line notes with Shift+Enter, and prefer the Button loading prop
…tia so the unsaved-changes dialog asks first
…pen, and cover card actions on a Discord name and reel meta in webhooks
…ge, time zone offset, language, channel and board pickers

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Instagram collaborators on Reels/feed posts

1 participant