<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Anton Ships]]></title><description><![CDATA[Anton Ships]]></description><link>https://antonships.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Anton Ships</title><link>https://antonships.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 13 Oct 2026 13:53:02 GMT</lastBuildDate><atom:link href="https://antonships.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Social media API approval: OAuth, review, and publishing]]></title><description><![CDATA[Google OAuth verification and Meta app review were in different states on 27 September 2026. Neither screen establishes YouTube API audit approval or a successful public post. Screenshots by Anton Ros]]></description><link>https://antonships.hashnode.dev/social-media-api-approval-workflow</link><guid isPermaLink="true">https://antonships.hashnode.dev/social-media-api-approval-workflow</guid><category><![CDATA[APIs]]></category><category><![CDATA[oauth]]></category><category><![CDATA[SaaS]]></category><category><![CDATA[software development]]></category><category><![CDATA[social media]]></category><dc:creator><![CDATA[Anton Rost]]></dc:creator><pubDate>Sun, 04 Oct 2026 12:23:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abb708559cab370b1573edc/31e9d050-9ead-4fa9-9e72-d5c123a9a51c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6abb708559cab370b1573edc/529954ab-315a-44ca-a763-c492d2f41e31.png" alt="Meta's app dashboard shows unfinished review steps beside Google Auth Platform's verified branding and data-access status." style="display:block;margin:0 auto" />

<p><em>Google OAuth verification and Meta app review were in different states on 27 September 2026. Neither screen establishes YouTube API audit approval or a successful public post. Screenshots by Anton Rost.</em></p>
<p>A connected account is the beginning of a publishing job, not evidence that the job can be completed. The callback may succeed while the application still lacks provider approval, the selected destination lacks a capability, or an uploaded video remains private.</p>
<p>While building Sendezeit, I realised that “ready” could mean three different things: an account was connected, the app had provider approval, or a post was actually live. I needed to stop treating those as the same milestone. A more useful approach was to follow the work from authorization to the result the user actually receives, checking what each stage proves before moving to the next.</p>
<p>The phrase social media approval workflow sometimes describes teammates signing off on content. Here it means the provider-side authorization and review needed to deliver that content; it is not a description of a team-approval feature in Sendezeit.</p>
<p>The checks below follow the publishing job in order. They start with the connection, then move through the destination, submission, unfinished provider work, and the evidence needed to describe a result honestly.</p>
<h2>1. A reliable publishing path starts with the connection</h2>
<p>Before checking a post, make the authorization path unambiguous. The public application URL is where the user works, the OAuth redirect URI receives the browser after consent, and a webhook endpoint receives later provider events without that browser session. These addresses serve different purposes.</p>
<p>Register the production redirect accurately rather than assuming that a development callback, alternate hostname, or different path will be accepted. The OAuth security best-current-practice document calls for exact redirect matching, with a specific exception for localhost ports in native applications. It also addresses PKCE and protection against authorization-code attacks. <a href="https://www.rfc-editor.org/rfc/rfc9700.html">RFC 9700</a>.</p>
<p>Tie the authorization flow to an expiring server-side record containing the provider, initiating user, and allowed return path. Validate the callback against that record and prevent reuse. Apply the provider's documented PKCE requirements rather than copying one adapter's parameters into every other adapter.</p>
<p>For example, X's OAuth 2.0 authorization-code flow uses PKCE, and its <code>offline.access</code> scope enables refresh-token access. A connection that works now does not by itself establish that the application can keep working after the access token expires. <a href="https://docs.x.com/fundamentals/authentication/oauth-2-0/authorization-code">X authorization documentation</a>.</p>
<p>Keep token exchange and confidential client credentials on the server, protect stored tokens, and keep them out of logs. Test expiry, revocation, and disconnection alongside the successful path. Once access is established, the next check is where that access can actually be used.</p>
<h2>2. Access must lead to a specific destination and capability</h2>
<p>The person granting consent may manage several Pages or channels. Discover the destinations permitted by the grant and make the selected destination visible before submission. Otherwise, a successful connection can hide uncertainty about where the post will go.</p>
<p>Record the stable provider identifier, relevant granted permissions, and capability restrictions for that destination. Test the account type and login route you intend to support: one working professional account is not evidence of support for every account type.</p>
<p>A connection screen should therefore answer both “Which account is this?” and “What can the application do with it?” Leaving the second question unanswered pushes the discovery of restrictions into the publishing flow, after the user has already prepared their content.</p>
<p>With a destination and usable capability identified, the application can record a publishing attempt without confusing that attempt with its eventual result.</p>
<h2>3. Submission records the attempt, not necessarily publication</h2>
<p>Create the local attempt before making the external write. Link it to the reviewed content version, destination, media, and requested visibility, then retain the provider's response and identifiers when they arrive. This gives later status checks a specific job to reconcile.</p>
<p>In the Sendezeit code checked for this article, a submission returns <code>accepted</code> or <code>published</code>. Subsequent provider checks use <code>processing</code>, <code>published</code>, <code>needs_action</code>, or <code>failed</code>. The content lifecycle is a separate layer, with states such as <code>scheduled</code> and <code>publishing</code>.</p>
<p>Those names are implementation labels, not interchangeable proofs. An identifier may establish that a provider knows about the job without establishing that an ordinary viewer can see the content. The YouTube adapter's processing-complete result, in particular, is not an independent public-visibility check.</p>
<p>Keep requested visibility, reported provider status, and verified audience access as separate observations. Public, unlisted, and private are not one successful-upload category: an unlisted video can be accessible by link without being publicly discoverable. The next step is to determine what remains unfinished for the particular provider.</p>
<h2>4. The remaining work depends on the provider and mode</h2>
<p>TikTok's Upload Content flow makes this visible through <code>SEND_TO_USER_INBOX</code>. That status leaves the creator with a native step to complete; <code>PUBLISH_COMPLETE</code> is a later outcome. Publicly available post identifiers also depend on the documented public-viewership and moderation conditions. <a href="https://developers.tiktok.com/docs/en/content-posting-api-reference-get-video-status">TikTok status reference</a>.</p>
<p>An inbox handoff should not silently replace direct publishing because the two modes leave the user with different work. Provider approval matters here too: unaudited TikTok Direct Post clients are restricted to private viewing. Implementing the adapter does not remove that restriction. <a href="https://developers.tiktok.com/docs/en/content-posting-api-get-started">TikTok Direct Post setup</a>.</p>
<p>The capture below establishes an earlier stage, not either completed outcome. It shows an unconnected preview and a disabled draft button. Its caption needs to respect that boundary just as the product's status message does.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6abb708559cab370b1573edc/7152c601-e930-47f6-8b26-6aad96beb19c.png" alt="TikTok implementation notes beside an unconnected Sendezeit composer labelled Preview only and Not published, with Create draft disabled." style="display:block;margin:0 auto" />

<p><em>This 27 September capture shows an unconnected preview with a disabled draft button, not a delivered draft. Screenshots by Anton Rost.</em></p>
<p>YouTube has a different sequence. The upload response may arrive before video processing finishes, so processing status must be checked separately. <a href="https://developers.google.com/youtube/v3/guides/implementation/videos">YouTube video implementation guide</a>.</p>
<p>Google OAuth verification also differs from the YouTube API compliance audit. YouTube documents private-viewing restrictions for uploads from unverified API projects created after 28 July 2020, with an audit required to lift them. The verified consent-screen capture at the start of this article is not evidence that this audit passed. <a href="https://developers.google.com/youtube/v3/docs/videos/insert">YouTube upload reference</a>.</p>
<p>These intermediate stages make a missing response especially important to handle carefully. The absence of a result in the application is not always evidence that the provider did nothing.</p>
<h2>5. An uncertain outcome needs reconciliation before a retry</h2>
<p>A timeout can leave the provider's write completed while the application lacks its response. Repeating every failed-looking request risks creating duplicate posts, so preserve the uncertain attempt instead of immediately treating it as a failed publication.</p>
<p>Use a stable local attempt identity, without assuming that every provider honours a client-supplied idempotency key. Reconcile through the documented status endpoint, returned identifiers, or destination checks before deciding whether another write is safe.</p>
<p>Webhooks and polling can provide overlapping evidence about the same attempt. Follow the provider's signature, event, and replay rules rather than applying a universal signature recipe. Processing the same evidence twice should not create another publication or count another success.</p>
<p>If reconciliation is impossible, explain the uncertainty and offer a controlled next step. “Needs checking” is more useful than a confident success badge attached to a possible duplicate. Once individual attempts can be tracked this way, the release decision still needs a broader check: whether the path is available to the customers who will use it.</p>
<h2>6. A founder's successful test is not the release decision</h2>
<p>A provider may allow a founder or administrator to do something an ordinary external account cannot. Working code and a successful test therefore need to sit alongside production configuration, provider approval, and account-specific evidence.</p>
<p>The September captures show different dashboard states, not a completed availability audit. I use these five questions to keep that distinction visible:</p>
<table>
<thead>
<tr>
<th>Question</th>
<th>Evidence to retain</th>
</tr>
</thead>
<tbody><tr>
<td>Is the adapter implemented?</td>
<td>The supported path in code and its tests.</td>
</tr>
<tr>
<td>Is production configured?</td>
<td>The correct app identity, redirect, scopes, and mode.</td>
</tr>
<tr>
<td>Is required access approved?</td>
<td>The provider's approval or audit record for that capability.</td>
</tr>
<tr>
<td>Can an ordinary external account use it?</td>
<td>A dated test outside the founder/admin role.</td>
</tr>
<tr>
<td>Did the intended result happen?</td>
<td>Provider status, actual visibility, destination, and result identifier or URL.</td>
</tr>
</tbody></table>
<p>This is a checklist, not a claim that every Sendezeit destination passes all five checks. Name the account type, requested mode, observed result, and date in the release record. That prevents an old administrator test from becoming a broad promise about current customer access.</p>
<p>Prepare the reviewer journey from the real product: a stable entry point, reproducible credentials where requested, reasons for each permission, and a recording of the supported path. Privacy, retention, disconnection, and deletion information should match the application.</p>
<p>A release record built around these checks gives the interface a sounder basis for its final message. It can describe the stage reached without borrowing certainty from an earlier milestone.</p>
<h2>7. Tell the user which part of the job actually finished</h2>
<p>Before showing success, check the destination and content version, processing status, actual visibility, and any remaining native step. Retain the result identifier or URL where available, together with the verification time. A URL alone is not a visibility test.</p>
<p>If authorization is all that completed, say connected. If the provider accepted an upload, say accepted or processing. If the creator must finish in the native app, make that step explicit. Reserve a public-access claim for evidence of public access.</p>
<p>Following the job in this order prevents the earliest green checkmark from standing in for the last. A social publishing platform should save the user from decoding several provider dashboards without hiding the differences between them.</p>
<p>I am building <a href="https://sendezeit.app/">Sendezeit</a> around that separation. This guide describes the implementation and review approach, not a guarantee that every provider is approved for every account or publishing mode.</p>
<p><em>Implementation checked and linked documentation reviewed on 3 October 2026. Screenshots record 27 September 2026; provider requirements and account access can change.</em></p>
]]></content:encoded></item><item><title><![CDATA[I Had Plenty of Content Ideas and Almost Nothing Published]]></title><description><![CDATA[I made separate Instagram and YouTube upload documents for the same Pocket Orbit videos.
They were supposed to stay in sync, which sounds perfectly reasonable until two files have had a few months to ]]></description><link>https://antonships.hashnode.dev/i-had-plenty-of-content-ideas-and-almost-nothing-published</link><guid isPermaLink="true">https://antonships.hashnode.dev/i-had-plenty-of-content-ideas-and-almost-nothing-published</guid><category><![CDATA[content creation]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[social media]]></category><category><![CDATA[SaaS]]></category><dc:creator><![CDATA[Anton Rost]]></dc:creator><pubDate>Tue, 29 Sep 2026 08:52:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abb708559cab370b1573edc/e0245a92-12a4-434a-bf90-83130776036c.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I made separate Instagram and YouTube upload documents for the same Pocket Orbit videos.</p>
<p>They were supposed to stay in sync, which sounds perfectly reasonable until two files have had a few months to develop independent lives.</p>
<p>When I opened them again, the lists no longer matched. The Instagram document contained entries the YouTube one did not, and one of its rows still carried a YouTube tracking link. The calendar held yet another version of the week, although a row could mean planned, drafted, scheduled, attempted, or actually public depending on when I last remembered to update it.</p>
<p>One source library. Two upload documents. Several answers to what existed.</p>
<p><img src="https://sendezeit.app/articles/creator-content-workflow/original-playbooks-1440x810.webp" alt="Obsidian showing the original Pocket Orbit Instagram and YouTube upload playbooks side by side. The Instagram file says it covers the same 19 videos as the YouTube file." /></p>
<p><em>The same source library ended up with two separate upload playbooks.</em></p>
<p><em>Screenshots: Anton Rost. Source: original Pocket Orbit upload playbooks opened in Obsidian, 20 September 2026.</em></p>
<p>I blamed the documents first. Then I followed one post through the rest of the workflow and found the same drift in the calendar, the exports, and the upload state.</p>
<p>I could decide what to make. Getting the right version all the way to a public URL still depended on my memory.</p>
<h2>I kept fixing the research because it was easier</h2>
<p>The research system was genuinely useful. It started with phrases people were already searching around iPad notes, Apple Pencil workflows, lecture notes, handwriting, organisation, summaries, exports, and study methods.</p>
<p>I kept a knowledge base behind those phrases because I did not want a search term to turn straight into a confident answer I could not support. A query could be popular and still need better evidence, fall outside Pocket Orbit, or require review from somebody who actually spoke the language.</p>
<p>By then, the registry had grown into hundreds of search queries. A much smaller set had enough evidence to move into production.</p>
<p><code>Ready</code> still only meant I could start making the thing.</p>
<p>So I kept returning to the registry. Cleaning another row felt manageable. Finishing, adapting, and publishing the work attached to it did not.</p>
<h2>Then I counted the finished work</h2>
<p>There were three completed carousels.</p>
<p>Each one had five slides: a hook based on the search query, three useful answer slides, and one action at the end. Pocket Orbit could appear once when it genuinely helped with the answer.</p>
<p>Those fifteen finished slides still required visual research, copy, real product screens, claim checks, layout, exports, and upload preparation.</p>
<p>Clearing the research check only put a topic at the start of production. It still needed a script, visuals, exports, and a version for each platform.</p>
<p><img src="https://sendezeit.app/articles/creator-content-workflow/registry-and-tiktok-1440x810.webp" alt="The original Pocket Orbit query production registry open in Obsidian beside the live @antonships.app TikTok profile and its published carousel grid." /></p>
<p><em>The registry kept growing. The smaller grid on the right is what actually made it live.</em></p>
<p><em>Screenshots: Anton Rost. Sources: Pocket Orbit query production registry in Obsidian and @antonships.app on TikTok, 20 September 2026.</em></p>
<h2>Every file remembered a different version</h2>
<p>One recording could become a TikTok edit, an Instagram caption, a YouTube title and description, sometimes a Facebook version, and a few notes about what I wanted to change later.</p>
<p>Updating one version did not update the others. Moving a date in the <a href="https://sendezeit.app/product/social-media-calendar">calendar</a> did not prove the file was ready. A successful upload did not necessarily mean somebody else could see the post.</p>
<p>Planned, scheduled, attempted, and public were quietly becoming the same state.</p>
<p>The calendar looked organised because Tuesday had a row. It still could not tell me what had actually happened.</p>
<h2>I was reusing the wrong thing</h2>
<p>I had treated the final caption as the reusable unit. It should have been the real source underneath it.</p>
<p>That source might be a Pocket Orbit feature, a TikTok result, a paywall failure, or something I noticed while building after work. The event and its evidence should remain stable while the presentation changes for each destination.</p>
<p>On X, I can explain the reasoning. On TikTok, the useful visual has to arrive almost immediately. Instagram may use similar footage but still needs its own framing. YouTube needs another title, description, thumbnail, and link setup.</p>
<p>Copying one finished caption into every box was quick, but it flattened the idea into a version that fit nowhere particularly well.</p>
<p>The workflow I needed was simple enough to explain:</p>
<ol>
<li><strong>One real source</strong></li>
<li><strong>Versions for each platform</strong></li>
<li><strong>Review</strong></li>
<li><strong>Schedule</strong></li>
<li><strong>Check what really went live</strong></li>
</ol>
<p>I was still the only thing keeping those pieces connected.</p>
<h2>That became Sendezeit</h2>
<p>Sendezeit starts with something that actually happened, then keeps the <a href="https://sendezeit.app/product/adapt">destination versions</a>, edits, approvals, calendar entries, and <a href="https://sendezeit.app/product/publish">provider results</a> attached to that source.</p>
<p>It does not fix the Pocket Orbit paywall or explain why somebody watched a TikTok and did not subscribe. Those questions still belong in the product, RevenueCat, and the analytics.</p>
<p>Sendezeit cuts out that weekly scavenger hunt. One source sits beside its X, TikTok, and YouTube versions. Each one keeps its own copy, media, review, schedule, and final publication result. I no longer have to remember which draft was final, reopen every platform to check it, or compare a calendar with a folder full of exports.</p>
<p><img src="https://sendezeit.app/articles/creator-content-workflow/sendezeit-source-variants-1440x810.webp" alt="The live Sendezeit composer showing one source attached to separate X, TikTok, and YouTube drafts, with the YouTube preview open." /></p>
<p><em>The replacement: one source, separate destination drafts, and the publishing state kept together.</em></p>
<p><em>Screenshot: Anton Rost. Source: live Sendezeit workspace, 20 September 2026.</em></p>
<p>You can run the same method in a spreadsheet: keep one source, write each platform version separately, review it, and record the public result beside the plan. I built Sendezeit because I was tired of maintaining those joins myself.</p>
<p>I had spent most of my time improving research because it was easy to organise. Meanwhile, I kept losing time at the other end, working out which version went where and whether it really went live. That gave me one clear requirement for the product: keep a reliable record of what I published.</p>
<p>Sendezeit is live now. <a href="https://sendezeit.app/integrations/x-twitter">X</a> and <a href="https://sendezeit.app/integrations/youtube">YouTube</a> have passed real-connection and provider-confirmed publishing checks. <a href="https://sendezeit.app/integrations/tiktok">TikTok</a> uses Direct Post when the account is eligible or an explicit draft handoff when it is not. Instagram and Facebook stay labelled coming soon while Meta review finishes.</p>
]]></content:encoded></item></channel></rss>