<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Robert Saylor</title>
    <description>The latest articles on DEV Community by Robert Saylor (@robert_saylor_c9db74549e2).</description>
    <link>https://dev.to/robert_saylor_c9db74549e2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2171165%2F45f5db5d-578c-4e01-9bc7-0408c1b11dc1.jpg</url>
      <title>DEV Community: Robert Saylor</title>
      <link>https://dev.to/robert_saylor_c9db74549e2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/robert_saylor_c9db74549e2"/>
    <language>en</language>
    <item>
      <title>How to Build a ChatGPT GPT Action with Laravel, OAuth, and Two Separate Applications</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Mon, 24 Aug 2026 14:46:52 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/how-to-build-a-chatgpt-gpt-action-with-laravel-oauth-and-two-separate-applications-34d9</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/how-to-build-a-chatgpt-gpt-action-with-laravel-oauth-and-two-separate-applications-34d9</guid>
      <description>&lt;p&gt;What if your users could operate your Laravel application simply by talking to ChatGPT?&lt;/p&gt;

&lt;p&gt;Not by copying information from ChatGPT into your application.&lt;/p&gt;

&lt;p&gt;Not by embedding another chatbot into your website.&lt;/p&gt;

&lt;p&gt;I mean allowing ChatGPT to securely authenticate as a user and execute real actions against your Laravel API.&lt;/p&gt;

&lt;p&gt;I recently worked through exactly this type of integration while building a GPT Action for a production Laravel application.&lt;/p&gt;

&lt;p&gt;The final architecture allows a user to say something as simple as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show me my connected social channels.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT authenticates through OAuth, calls the Laravel API, resolves the OAuth identity to the correct application user, verifies the requested scope, and returns that user's actual data.&lt;/p&gt;

&lt;p&gt;The architecture looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatGPT
    ↓
Custom GPT Action
    ↓
OAuth 2.0
    ↓
Laravel Authentication Application
    ↓
Laravel Passport
    ↓
Account Mapping
    ↓
Main Laravel Application
    ↓
Application API
    ↓
User's Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;Getting all of those pieces working together, however, exposed several implementation details that aren't immediately obvious when you start experimenting with GPT Actions.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk through the architecture, OAuth flow, OpenAPI schema, Laravel Passport configuration, account linking, scopes, debugging, deployment problems, and one very important ChatGPT account limitation you should understand before planning to distribute a GPT publicly.&lt;/p&gt;

&lt;p&gt;And if you're reading this because you want to add ChatGPT Actions, AI integrations, OAuth, APIs, or other custom functionality to an existing Laravel application, that's exactly the kind of work we do at &lt;strong&gt;Custom PHP Design&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Custom Laravel and PHP Development:&lt;/strong&gt;  &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;
&lt;/h2&gt;

&lt;h1&gt;
  
  
  What Is a GPT Action?
&lt;/h1&gt;

&lt;p&gt;A custom GPT can do much more than generate text.&lt;/p&gt;

&lt;p&gt;GPT Actions allow a GPT to communicate with external APIs.&lt;/p&gt;

&lt;p&gt;That means a GPT can potentially:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieve customer account information&lt;/li&gt;
&lt;li&gt;Query application data&lt;/li&gt;
&lt;li&gt;Create records&lt;/li&gt;
&lt;li&gt;Update records&lt;/li&gt;
&lt;li&gt;Start workflows&lt;/li&gt;
&lt;li&gt;Publish content&lt;/li&gt;
&lt;li&gt;Retrieve reports&lt;/li&gt;
&lt;li&gt;Schedule tasks&lt;/li&gt;
&lt;li&gt;Interact with internal business systems&lt;/li&gt;
&lt;li&gt;Trigger Laravel jobs and services&lt;/li&gt;
&lt;li&gt;Work with existing SaaS functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The GPT learns what your application can do through an &lt;strong&gt;OpenAPI specification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.1.0&lt;/span&gt;

&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Example&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Application&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API"&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;

&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://example.com&lt;/span&gt;

&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/api/v1/channels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;getConnectedChannels&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get the authenticated user's connected channels&lt;/span&gt;

      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connected&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;channels"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once imported into the GPT Action configuration, ChatGPT understands that it has an operation named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getConnectedChannels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can decide to call that operation when the user asks something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What accounts do I have connected?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This creates a very interesting interface for existing Laravel applications.&lt;/p&gt;

&lt;p&gt;Instead of forcing every workflow through forms, dashboards, and navigation menus, some workflows can become conversational.&lt;/p&gt;

&lt;p&gt;That doesn't mean replacing your application interface with ChatGPT.&lt;/p&gt;

&lt;p&gt;It means giving customers &lt;strong&gt;another way to interact with the functionality you've already built&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you already have a mature Laravel application and want to explore this type of integration, visit:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Why OAuth Matters
&lt;/h1&gt;

&lt;p&gt;For public application data, a GPT Action can potentially operate without user authentication.&lt;/p&gt;

&lt;p&gt;But most useful SaaS applications contain &lt;strong&gt;user-specific data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose two customers use your GPT.&lt;/p&gt;

&lt;p&gt;Customer A should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer A's data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Customer B should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer B's data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Customer A must never be able to access Customer B's information.&lt;/p&gt;

&lt;p&gt;The GPT therefore needs to authenticate each user independently.&lt;/p&gt;

&lt;p&gt;That's where OAuth comes in.&lt;/p&gt;

&lt;p&gt;A typical OAuth Action requires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client ID
Client Secret
Authorization URL
Token URL
Scopes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Laravel, &lt;strong&gt;Laravel Passport&lt;/strong&gt; is a natural option because it provides a full OAuth2 server implementation.&lt;/p&gt;

&lt;p&gt;Our configuration ultimately looked conceptually like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization URL:
https://auth.example.com/oauth/authorize

Token URL:
https://auth.example.com/oauth/token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GPT receives its own OAuth client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client ID
Client Secret
Redirect URI
Grant Types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important grant types were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;authorization_code
refresh_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The refresh token is particularly important because you don't want customers authenticating again every time ChatGPT needs to call your API.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why We Used Two Laravel Applications
&lt;/h1&gt;

&lt;p&gt;This implementation had an additional architectural challenge.&lt;/p&gt;

&lt;p&gt;The primary application and OAuth server were &lt;strong&gt;separate Laravel codebases&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of them as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main-application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auth-application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main application contained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer accounts&lt;/li&gt;
&lt;li&gt;Business logic&lt;/li&gt;
&lt;li&gt;API endpoints&lt;/li&gt;
&lt;li&gt;User data&lt;/li&gt;
&lt;li&gt;Application permissions&lt;/li&gt;
&lt;li&gt;Existing API authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Auth application contained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Laravel Passport&lt;/li&gt;
&lt;li&gt;OAuth clients&lt;/li&gt;
&lt;li&gt;Authorization codes&lt;/li&gt;
&lt;li&gt;Access tokens&lt;/li&gt;
&lt;li&gt;Refresh tokens&lt;/li&gt;
&lt;li&gt;OAuth authorization&lt;/li&gt;
&lt;li&gt;External identity mapping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several reasons you might end up with this architecture.&lt;/p&gt;

&lt;p&gt;You might want authentication isolated from the main application.&lt;/p&gt;

&lt;p&gt;You may already have a separate authentication service.&lt;/p&gt;

&lt;p&gt;You may be modernizing a legacy Laravel application.&lt;/p&gt;

&lt;p&gt;You may want several applications to eventually use the same OAuth provider.&lt;/p&gt;

&lt;p&gt;Whatever the reason, separating the applications creates an important question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How does an OAuth user in the Auth application map back to the correct user in the main Laravel application?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That became one of the most important parts of this integration.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Account Linking Problem
&lt;/h1&gt;

&lt;p&gt;Imagine this situation.&lt;/p&gt;

&lt;p&gt;Your primary Laravel application contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Laravel Application

users
----------------
id = 123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your OAuth application has its own users table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auth Laravel Application

users
----------------
id = 456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ChatGPT receives an OAuth access token associated with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auth User 456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But your main application's API needs to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Application User 123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those aren't inherently the same identity.&lt;/p&gt;

&lt;p&gt;You need a trusted mapping between them.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auth User 456
        ↓
account_links
        ↓
Application User 123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A linking table can represent that relationship:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application_account_links

auth_user_id
application_user_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then OAuth token introspection can return something conceptually like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"linked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"application_user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"channels.read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"posts.read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"posts.write"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main Laravel application can now authenticate the incoming API request as User 123.&lt;/p&gt;

&lt;p&gt;This is the bridge between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatGPT OAuth identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your actual Laravel customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Never Trust a Browser-Supplied User ID
&lt;/h1&gt;

&lt;p&gt;This part is critical.&lt;/p&gt;

&lt;p&gt;You should never build account linking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/connect?user_id=123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and blindly trust that ID.&lt;/p&gt;

&lt;p&gt;An attacker could simply change it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/connect?user_id=124
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, the main Laravel application should &lt;strong&gt;cryptographically assert the identity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We accomplished this with a short-lived signed handoff.&lt;/p&gt;




&lt;h1&gt;
  
  
  Signed Laravel-to-Laravel Account Handoff
&lt;/h1&gt;

&lt;p&gt;The main application generates a short-lived signed token containing claims such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auth.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1787540000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1787540300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jti"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unique-random-value"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important claim is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That represents the canonical user ID from the main application.&lt;/p&gt;

&lt;p&gt;The token is signed using RSA SHA-256:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RS256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Laravel Application

Private Key
    ↓
Sign handoff token
    ↓

Auth Laravel Application

Public Key
    ↓
Verify signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The private signing key remains with the application generating the identity assertion.&lt;/p&gt;

&lt;p&gt;The Auth application only needs the public key to verify it.&lt;/p&gt;

&lt;p&gt;This gives the Auth application cryptographic proof that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The main application says this request belongs to User 123.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's considerably safer than trusting browser-supplied identifiers.&lt;/p&gt;




&lt;h1&gt;
  
  
  Validate More Than the Signature
&lt;/h1&gt;

&lt;p&gt;Verifying the RSA signature is necessary.&lt;/p&gt;

&lt;p&gt;It isn't sufficient.&lt;/p&gt;

&lt;p&gt;The Auth application should validate claims such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alg
kid
iss
aud
iat
exp
sub
jti
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alg = RS256
issuer = expected application
audience = expected Auth service
expiration = still valid
subject = valid canonical user ID
jti = unique
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not allow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alg = none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not silently accept an unexpected algorithm.&lt;/p&gt;

&lt;p&gt;Do not accept expired handoffs.&lt;/p&gt;

&lt;p&gt;Do not trust the subject until after signature verification.&lt;/p&gt;

&lt;p&gt;The point of the handoff is to create a &lt;strong&gt;small, explicit trust boundary&lt;/strong&gt; between the two Laravel applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Prevent Replay Attacks
&lt;/h1&gt;

&lt;p&gt;A valid signed handoff should generally be redeemable only once.&lt;/p&gt;

&lt;p&gt;That's why we included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jti
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auth application records each redeemed JTI.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;handoff_redemptions

id
jti
application_user_id
redeemed_by_auth_user_id
redeemed_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database should enforce uniqueness on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jti
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if somebody attempts to reuse the exact same handoff, Auth can reject it.&lt;/p&gt;

&lt;p&gt;This provides replay protection even if somebody somehow obtains a previously valid handoff URL.&lt;/p&gt;




&lt;h1&gt;
  
  
  The User Shouldn't Know You Have Two Laravel Applications
&lt;/h1&gt;

&lt;p&gt;Our initial implementation exposed an interesting UX problem.&lt;/p&gt;

&lt;p&gt;The Auth application behaved like a normal standalone Laravel application.&lt;/p&gt;

&lt;p&gt;A user arriving without an Auth session would see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, that made sense.&lt;/p&gt;

&lt;p&gt;From a product perspective, it didn't.&lt;/p&gt;

&lt;p&gt;The customer already has an account in the main application.&lt;/p&gt;

&lt;p&gt;Why should they create another account and another password just because we decided to use a separate OAuth service internally?&lt;/p&gt;

&lt;p&gt;They shouldn't.&lt;/p&gt;

&lt;p&gt;The better experience is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User logs into main Laravel application
        ↓
Connect ChatGPT
        ↓
Main application creates signed handoff
        ↓
Auth verifies handoff
        ↓
Auth resolves or provisions internal identity
        ↓
Account mapping created
        ↓
OAuth authorization continues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auth user becomes an &lt;strong&gt;implementation detail&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The customer doesn't need to know it exists.&lt;/p&gt;

&lt;p&gt;This is an important lesson when building authentication systems.&lt;/p&gt;

&lt;p&gt;There is a difference between:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;technically correct authentication&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;good authentication UX.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Automatically Provisioning the Internal OAuth User
&lt;/h1&gt;

&lt;p&gt;If your OAuth server requires its own local user record, you can automatically provision that internal identity after verifying the signed handoff.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AccountLink&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'application_user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$verifiedSubject&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$authUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$link&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;authUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$authUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createInternalAuthUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;AccountLink&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'auth_user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$authUser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'application_user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$verifiedSubject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual implementation should be transactional and enforce uniqueness.&lt;/p&gt;

&lt;p&gt;The important idea is that the user doesn't have to manually register with your OAuth service.&lt;/p&gt;

&lt;p&gt;Their identity has already been established by the trusted main application.&lt;/p&gt;




&lt;h1&gt;
  
  
  OAuth Scopes Are Worth Doing Properly
&lt;/h1&gt;

&lt;p&gt;Don't give the GPT unlimited API access.&lt;/p&gt;

&lt;p&gt;Use OAuth scopes.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;profile
channels.read
posts.read
posts.write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Laravel API routes can then require specific scopes.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/connected-channels'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'external-scope:channels.read'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Publishing endpoints might require:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts.write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading existing posts might require:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts.read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a clean permission boundary.&lt;/p&gt;

&lt;p&gt;If you later introduce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;analytics.read
billing.read
account.write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;those capabilities can remain unavailable unless explicitly authorized.&lt;/p&gt;

&lt;p&gt;This is especially important with AI integrations because you should expose &lt;strong&gt;only the capabilities the AI client actually needs&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Your Existing Laravel API Doesn't Have to Be Replaced
&lt;/h1&gt;

&lt;p&gt;Our main application already had its own API authentication.&lt;/p&gt;

&lt;p&gt;We didn't want ChatGPT OAuth support to break or replace existing API clients.&lt;/p&gt;

&lt;p&gt;Instead, the API middleware could support multiple authentication paths.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming API Request
        ↓
Existing API key?
        ↓ yes
Authenticate normally

        ↓ no

External OAuth bearer token?
        ↓
Ask Auth application to introspect token
        ↓
Resolve application user
        ↓
Apply OAuth scopes
        ↓
Continue request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows existing integrations to continue working while ChatGPT uses OAuth.&lt;/p&gt;

&lt;p&gt;That's an important architectural principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add AI integration capabilities without unnecessarily rewriting working application infrastructure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Custom PHP Design&lt;/strong&gt;, this is how we approach Laravel modernization projects: integrate with what already works instead of automatically assuming the entire application needs to be rebuilt.&lt;/p&gt;

&lt;p&gt;Learn more:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Token Introspection Between Laravel Applications
&lt;/h1&gt;

&lt;p&gt;The main application should not directly query Passport's database.&lt;/p&gt;

&lt;p&gt;That would tightly couple the two applications.&lt;/p&gt;

&lt;p&gt;Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Application
       ↓
Server-to-server request
       ↓
Auth Application
       ↓
Passport token validation
       ↓
Account mapping
       ↓
Introspection response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request should use a server-to-server secret.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-Application-Auth-Secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser never sees this value.&lt;/p&gt;

&lt;p&gt;ChatGPT never sees this value.&lt;/p&gt;

&lt;p&gt;It exists strictly between your servers.&lt;/p&gt;

&lt;p&gt;The main application sends the OAuth access token to the Auth service for validation.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oauth-access-token"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auth application returns something similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"linked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"application_user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"channels.read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"posts.read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"posts.write"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your normal Laravel middleware can resolve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;findOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$applicationUserId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and continue processing the API request.&lt;/p&gt;




&lt;h1&gt;
  
  
  Don't Put Server Secrets in the Browser
&lt;/h1&gt;

&lt;p&gt;This deserves its own section.&lt;/p&gt;

&lt;p&gt;Your introspection secret should never appear in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JavaScript
HTML
Blade data attributes
localStorage
sessionStorage
query strings
frontend API calls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The communication should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatGPT
    ↓ bearer token
Main Laravel API
    ↓ private server-to-server request
Auth Laravel Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
    ↓ secret
Auth Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep internal authentication internal.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Tiny Header Name Cost Us Debugging Time
&lt;/h1&gt;

&lt;p&gt;One of the bugs we encountered was painfully simple.&lt;/p&gt;

&lt;p&gt;One Laravel application sent a header similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-Application-Introspection-Secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while the Auth application expected something similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-Application-Auth-Secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;401 Unauthorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything else was correct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The bearer token existed&lt;/li&gt;
&lt;li&gt;OAuth worked&lt;/li&gt;
&lt;li&gt;The account was linked&lt;/li&gt;
&lt;li&gt;The API route existed&lt;/li&gt;
&lt;li&gt;The scope existed&lt;/li&gt;
&lt;li&gt;The GPT was making the request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the server-to-server authentication failed before token introspection could happen.&lt;/p&gt;

&lt;p&gt;The lesson:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standardize internal authentication headers and configuration names.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Better yet, encapsulate server-to-server communication inside a service class instead of scattering raw HTTP requests throughout your Laravel application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Safe Diagnostic Logging Is Extremely Helpful
&lt;/h1&gt;

&lt;p&gt;When debugging OAuth, logging everything is tempting.&lt;/p&gt;

&lt;p&gt;Don't.&lt;/p&gt;

&lt;p&gt;Never log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;access_token
refresh_token
client_secret
private keys
shared secrets
authorization codes
signed handoff tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, log &lt;strong&gt;state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"external_introspection_attempted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"introspection_http_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"introspection_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"introspection_linked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resolved_application_user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"channels.read"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"auth_result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external_oauth"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells you almost everything you need without leaking credentials.&lt;/p&gt;

&lt;p&gt;During development, this was invaluable.&lt;/p&gt;

&lt;p&gt;We could watch the request move through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatGPT
→ Auth
→ Main API
→ Introspection
→ User resolution
→ Scope validation
→ Controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When something failed, we knew exactly which boundary was failing.&lt;/p&gt;




&lt;h1&gt;
  
  
  File Permissions Can Break OAuth Too
&lt;/h1&gt;

&lt;p&gt;Another production issue had almost nothing to do with OAuth protocol logic.&lt;/p&gt;

&lt;p&gt;Laravel Passport's RSA keys had incorrect filesystem permissions after deployment.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a Passport key ended up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;644
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OAuth library rejected it.&lt;/p&gt;

&lt;p&gt;The result appeared upstream as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;500 Internal Server Error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix was straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;www-data:www-data storage/oauth-private.key
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;www-data:www-data storage/oauth-public.key

&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 storage/oauth-private.key
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 storage/oauth-public.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But fixing it manually isn't enough.&lt;/p&gt;

&lt;p&gt;Your deployment process should enforce it.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Setting Laravel runtime permissions..."&lt;/span&gt;

&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data storage bootstrap/cache
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; ug+rwX storage bootstrap/cache

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; storage/oauth-private.key &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 storage/oauth-private.key
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; storage/oauth-public.key &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 storage/oauth-public.key
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also helps prevent another classic Laravel production error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;storage/logs/laravel.log: Permission denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production automation should leave your application in a known-good state after every deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  The GPT Action OAuth Configuration
&lt;/h1&gt;

&lt;p&gt;Once the backend architecture was ready, the GPT Action itself needed OAuth configuration.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authentication Type:
OAuth

Client ID:
&amp;lt;passport-client-id&amp;gt;

Client Secret:
&amp;lt;passport-client-secret&amp;gt;

Authorization URL:
https://auth.example.com/oauth/authorize

Token URL:
https://auth.example.com/oauth/token

Scopes:
profile channels.read posts.read posts.write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ChatGPT provides a callback URL.&lt;/p&gt;

&lt;p&gt;That callback must match the redirect URI registered with your OAuth server.&lt;/p&gt;

&lt;p&gt;Exactly.&lt;/p&gt;

&lt;p&gt;Not approximately.&lt;/p&gt;

&lt;p&gt;Not "same domain."&lt;/p&gt;

&lt;p&gt;Not "close enough."&lt;/p&gt;

&lt;p&gt;Exactly.&lt;/p&gt;

&lt;p&gt;If ChatGPT gives you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://chat.openai.com/aip/g-example/oauth/callback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then your Passport OAuth client needs that callback.&lt;/p&gt;




&lt;h1&gt;
  
  
  Be Careful When Recreating or Editing Your GPT
&lt;/h1&gt;

&lt;p&gt;During development, callback URLs can become a source of confusion.&lt;/p&gt;

&lt;p&gt;If the callback associated with the GPT changes, your Passport client can still contain the old redirect URI.&lt;/p&gt;

&lt;p&gt;Then ChatGPT sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redirect_uri = NEW_CALLBACK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while Passport expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OLD_CALLBACK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OAuth fails.&lt;/p&gt;

&lt;p&gt;Instead of constantly creating new clients and secrets, we built tooling to safely update the existing Passport client's redirect URI while preserving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client ID
Secret
Grant Types
Name
State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That proved extremely useful during development.&lt;/p&gt;

&lt;p&gt;The broader lesson is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build diagnostic and administrative tools around OAuth instead of manually editing database rows.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Passport Schemas Change Between Versions
&lt;/h1&gt;

&lt;p&gt;Another lesson: don't assume examples written for an older Laravel Passport version match your installation.&lt;/p&gt;

&lt;p&gt;For example, older examples may reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redirect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while newer Passport versions can use fields such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redirect_uris
grant_types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Laravel\Passport\Client&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'redirect'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'revoked'&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;against a schema that no longer contains &lt;code&gt;redirect&lt;/code&gt;, you'll get a database error.&lt;/p&gt;

&lt;p&gt;Always inspect the actual Passport version and schema you're using.&lt;/p&gt;

&lt;p&gt;Don't blindly copy an OAuth tutorial written several major versions ago.&lt;/p&gt;




&lt;h1&gt;
  
  
  Build Diagnostic Artisan Commands
&lt;/h1&gt;

&lt;p&gt;One of the best things we did during this project was build small Artisan commands specifically for OAuth diagnostics.&lt;/p&gt;

&lt;p&gt;For example, a command could inspect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client ID
Client name
Redirect URIs
Grant types
Revoked state
Client type
Secret storage
Exact redirect match
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even securely prompt for the client secret and verify whether it matches the stored hash without printing either value.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan oauth:inspect-clients &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s1"&gt;'CLIENT_ID'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--redirect-uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'EXACT_CALLBACK'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--verify-secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;State: active
Client type: confidential
Secret storage: hashed
Grant types: authorization_code, refresh_token
Exact redirect match: yes
Submitted secret matches: yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's dramatically better than guessing.&lt;/p&gt;




&lt;h1&gt;
  
  
  Your OpenAPI Schema Is the GPT's Map
&lt;/h1&gt;

&lt;p&gt;Authentication gets the GPT through the front door.&lt;/p&gt;

&lt;p&gt;Your OpenAPI specification tells it what it can actually do.&lt;/p&gt;

&lt;p&gt;Suppose your application exposes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;/api/v1/connected-channels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;getConnectedChannels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;/api/v1/posts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;createPost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The descriptions matter.&lt;/p&gt;

&lt;p&gt;Good:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get the authenticated user's connected social media channels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Less useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get channels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model needs enough semantic information to understand when an operation applies.&lt;/p&gt;

&lt;p&gt;Operation IDs should also be descriptive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getConnectedChannels
createSocialPost
scheduleSocialPost
getScheduledPosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get1
post2
action3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat your OpenAPI specification as part API contract and part AI interface.&lt;/p&gt;




&lt;h1&gt;
  
  
  Confirmation Before Destructive or External Actions
&lt;/h1&gt;

&lt;p&gt;AI-driven API access raises another UX consideration.&lt;/p&gt;

&lt;p&gt;Some operations should happen immediately.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show me my connected channels.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's read-only.&lt;/p&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Publish this to Facebook and Instagram.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;has an external side effect.&lt;/p&gt;

&lt;p&gt;For workflows like publishing, deleting, sending, purchasing, or modifying important records, design your Action and GPT instructions so the user has an opportunity to confirm what will happen.&lt;/p&gt;

&lt;p&gt;A good interaction might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User:
Create a post about our product launch for Facebook and Instagram.

GPT:
Here's the proposed post:

...

Would you like me to publish this to Facebook and Instagram?

User:
Yes.

GPT:
→ API Action
→ publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is better than making every conversational request immediately destructive.&lt;/p&gt;




&lt;h1&gt;
  
  
  Test the Entire Chain, Not Just OAuth
&lt;/h1&gt;

&lt;p&gt;An OAuth token successfully being issued does &lt;strong&gt;not&lt;/strong&gt; mean your integration works.&lt;/p&gt;

&lt;p&gt;We tested the entire path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatGPT
    ↓
Authorization request
    ↓
Laravel Passport
    ↓
Authorization code
    ↓
Token exchange
    ↓
Access token
    ↓
GPT Action request
    ↓
Main Laravel API
    ↓
External token introspection
    ↓
Account mapping
    ↓
Scope validation
    ↓
Correct application user
    ↓
Actual application data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment that mattered wasn't when Passport issued a token.&lt;/p&gt;

&lt;p&gt;It was when we could ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show me my connected social channels.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and receive the channels belonging to the correct application user.&lt;/p&gt;

&lt;p&gt;That proved the architecture end-to-end.&lt;/p&gt;




&lt;h1&gt;
  
  
  Test With More Than One User
&lt;/h1&gt;

&lt;p&gt;Before calling an OAuth integration production-ready, test user isolation.&lt;/p&gt;

&lt;p&gt;Create or use at least two application users.&lt;/p&gt;

&lt;p&gt;Then verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A
→ OAuth
→ User A's resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User B
→ OAuth
→ User B's resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User B
→ OAuth
→ User A's resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token refresh&lt;/li&gt;
&lt;li&gt;Expired tokens&lt;/li&gt;
&lt;li&gt;Revoked tokens&lt;/li&gt;
&lt;li&gt;Missing scopes&lt;/li&gt;
&lt;li&gt;Invalid scopes&lt;/li&gt;
&lt;li&gt;Missing account mapping&lt;/li&gt;
&lt;li&gt;Conflicting account mapping&lt;/li&gt;
&lt;li&gt;Replayed handoff&lt;/li&gt;
&lt;li&gt;Auth service unavailable&lt;/li&gt;
&lt;li&gt;Invalid server-to-server secret&lt;/li&gt;
&lt;li&gt;Invalid OAuth client secret&lt;/li&gt;
&lt;li&gt;Incorrect redirect URI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy-path testing is not enough for authentication systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  The ChatGPT Account Limitation You Need to Know About
&lt;/h1&gt;

&lt;p&gt;Now for the frustrating part.&lt;/p&gt;

&lt;p&gt;You can build the entire integration correctly and still discover that you cannot distribute your GPT the way you expected.&lt;/p&gt;

&lt;p&gt;As of 2026, OpenAI's documentation states that &lt;strong&gt;new GPT creation and publishing are not available on personal ChatGPT accounts&lt;/strong&gt;, including:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Free
Go
Plus
Pro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Existing GPTs can remain available to their owners and can still be edited when the applicable plan and permissions allow it.&lt;/p&gt;

&lt;p&gt;However, creating and publishing GPTs is currently available through eligible managed workspaces such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Business
Enterprise
Edu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;subject to workspace permissions.&lt;/p&gt;

&lt;p&gt;This is important because upgrading from ChatGPT Plus to the much more expensive personal ChatGPT Pro plan does &lt;strong&gt;not&lt;/strong&gt; necessarily solve the GPT publishing problem.&lt;/p&gt;

&lt;p&gt;Pro is still a personal account.&lt;/p&gt;

&lt;p&gt;If your goal is to create a GPT that customers can access, you need to evaluate the current Business/Enterprise/Edu workspace requirements before investing significant development time.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why This Matters for Independent Developers and SaaS Companies
&lt;/h1&gt;

&lt;p&gt;This creates an interesting situation for solo developers and small SaaS companies.&lt;/p&gt;

&lt;p&gt;You may be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build the GPT&lt;/li&gt;
&lt;li&gt;Configure the Action&lt;/li&gt;
&lt;li&gt;Implement OAuth&lt;/li&gt;
&lt;li&gt;Test the API&lt;/li&gt;
&lt;li&gt;Use the GPT privately&lt;/li&gt;
&lt;li&gt;Prove the entire architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but still need an eligible managed ChatGPT workspace before you can distribute that GPT to customers.&lt;/p&gt;

&lt;p&gt;That doesn't make the development work useless.&lt;/p&gt;

&lt;p&gt;Far from it.&lt;/p&gt;

&lt;p&gt;Your OAuth-enabled API can potentially support many other clients.&lt;/p&gt;

&lt;p&gt;And your private GPT can serve as a beta environment while you validate whether customers actually want conversational access to your application.&lt;/p&gt;

&lt;p&gt;That's exactly how I would approach it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prove demand before adding another recurring expense.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  A Private GPT Can Still Be a Valuable Beta
&lt;/h1&gt;

&lt;p&gt;If you already have an existing GPT on a personal account, private testing can be extremely useful.&lt;/p&gt;

&lt;p&gt;You can validate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OAuth
Account linking
API authentication
Scopes
Token refresh
OpenAPI operations
Prompt behavior
Error handling
User confirmation
API response design
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before making the integration broadly available.&lt;/p&gt;

&lt;p&gt;That gives you a working prototype.&lt;/p&gt;

&lt;p&gt;If customers later start asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can I control this from ChatGPT?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you already know the backend architecture works.&lt;/p&gt;

&lt;p&gt;At that point, moving the GPT into an eligible managed workspace becomes a business decision rather than an experiment.&lt;/p&gt;




&lt;h1&gt;
  
  
  GPT Actions Are Really API Product Development
&lt;/h1&gt;

&lt;p&gt;One of my biggest takeaways from this project is that GPT Actions aren't primarily about prompt engineering.&lt;/p&gt;

&lt;p&gt;The hard part isn't writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a helpful social media assistant.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hard part is everything behind it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authentication
Authorization
OAuth
Account linking
Scopes
API design
OpenAPI
Security boundaries
Error handling
Deployment
User isolation
Token lifecycle
Observability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building a serious GPT Action is backend application development.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you already have a well-designed Laravel API, you're in a strong position.&lt;/p&gt;

&lt;p&gt;If you don't have an API yet, adding GPT Actions may expose architectural work that needs to happen first.&lt;/p&gt;




&lt;h1&gt;
  
  
  Laravel Is a Great Fit for GPT Action Integrations
&lt;/h1&gt;

&lt;p&gt;Laravel gives you many of the building blocks needed for this kind of project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Middleware&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Laravel Passport&lt;/li&gt;
&lt;li&gt;HTTP client&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Service containers&lt;/li&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Database transactions&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Configuration management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical architecture can remain very Laravel-native.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPT Action
    ↓
Laravel API Route
    ↓
Authentication Middleware
    ↓
Scope Middleware
    ↓
Controller
    ↓
Service
    ↓
Application Logic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need to throw away your existing Laravel architecture just because AI is involved.&lt;/p&gt;




&lt;h1&gt;
  
  
  You Probably Don't Need to Rewrite Your Laravel Application for AI
&lt;/h1&gt;

&lt;p&gt;This is another area where companies can easily overspend.&lt;/p&gt;

&lt;p&gt;Adding AI functionality doesn't necessarily mean rewriting your application around AI.&lt;/p&gt;

&lt;p&gt;If you already have a working Laravel application, you may only need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API endpoints
OAuth
OpenAPI schema
Identity mapping
AI-specific service layer
Additional authorization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your existing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Models
Services
Jobs
Queues
Database
Business logic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can often remain exactly where they are.&lt;/p&gt;

&lt;p&gt;The AI becomes another interface into the application.&lt;/p&gt;

&lt;p&gt;That's a much more practical way to modernize an existing system.&lt;/p&gt;




&lt;h1&gt;
  
  
  Need GPT Actions Added to Your Laravel Application?
&lt;/h1&gt;

&lt;p&gt;If you have an existing Laravel or custom PHP application and you're wondering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can ChatGPT securely interact with my application?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer may very well be yes.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Custom PHP Design&lt;/strong&gt;, we work with custom PHP and Laravel applications and can help design and implement integrations involving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ChatGPT GPT Actions&lt;/li&gt;
&lt;li&gt;OpenAI integrations&lt;/li&gt;
&lt;li&gt;Laravel APIs&lt;/li&gt;
&lt;li&gt;OpenAPI specifications&lt;/li&gt;
&lt;li&gt;OAuth 2.0&lt;/li&gt;
&lt;li&gt;Laravel Passport&lt;/li&gt;
&lt;li&gt;API authentication&lt;/li&gt;
&lt;li&gt;Account linking&lt;/li&gt;
&lt;li&gt;External SaaS integrations&lt;/li&gt;
&lt;li&gt;AI-assisted workflows&lt;/li&gt;
&lt;li&gt;Existing application modernization&lt;/li&gt;
&lt;li&gt;AWS deployments&lt;/li&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;li&gt;Laravel architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you have a modern Laravel application or a custom PHP system that's been evolving for years, you don't necessarily need to rebuild everything to take advantage of AI.&lt;/p&gt;

&lt;p&gt;Sometimes the right solution is to build a secure bridge between what you already have and what AI can now provide.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Learn more about Custom PHP Design:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're trying to determine whether GPT Actions can be integrated into your existing Laravel application, &lt;strong&gt;Custom PHP Design can help evaluate the architecture and build the integration.&lt;/strong&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  Final Architecture
&lt;/h1&gt;

&lt;p&gt;After working through the authentication, account linking, API middleware, introspection, and deployment issues, the architecture ultimately looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────┐
│        ChatGPT          │
│       Custom GPT        │
└────────────┬────────────┘
             │
             │ OAuth Authorization
             ▼
┌─────────────────────────┐
│   Laravel Auth Service  │
│                         │
│   Laravel Passport      │
│   OAuth Clients         │
│   Access Tokens         │
│   Refresh Tokens        │
└────────────┬────────────┘
             │
             │ Account Mapping
             ▼
┌─────────────────────────┐
│  Application Identity   │
│                         │
│ Auth User ↔ App User    │
└────────────┬────────────┘
             │
             │ Token Introspection
             ▼
┌─────────────────────────┐
│ Main Laravel Application│
│                         │
│ API Authentication      │
│ OAuth Scope Validation  │
│ Existing Business Logic │
└────────────┬────────────┘
             │
             ▼
┌─────────────────────────┐
│      Customer Data      │
└─────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And from the user's perspective?&lt;/p&gt;

&lt;p&gt;It can be as simple as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show me my connected social channels.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the part users see.&lt;/p&gt;

&lt;p&gt;Everything else is our job as developers.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;GPT Actions open up some genuinely interesting possibilities for existing Laravel applications.&lt;/p&gt;

&lt;p&gt;But don't mistake them for a simple prompt-engineering exercise.&lt;/p&gt;

&lt;p&gt;Once a GPT needs to securely access private customer data or perform actions on behalf of a user, you're dealing with traditional application engineering concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Identity
Authentication
Authorization
OAuth
API security
User isolation
Scopes
Token management
Infrastructure
Deployment
Observability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The good news is that Laravel is extremely well suited to solving those problems.&lt;/p&gt;

&lt;p&gt;And once the foundation exists, the conversational layer can be surprisingly powerful.&lt;/p&gt;

&lt;p&gt;A user doesn't necessarily need to know which API endpoint to call.&lt;/p&gt;

&lt;p&gt;They don't need to know your JSON structure.&lt;/p&gt;

&lt;p&gt;They don't need to know your controller names.&lt;/p&gt;

&lt;p&gt;They can simply explain what they want.&lt;/p&gt;

&lt;p&gt;ChatGPT determines the appropriate Action.&lt;/p&gt;

&lt;p&gt;Your Laravel application remains responsible for determining whether that action is authorized and executing it safely.&lt;/p&gt;

&lt;p&gt;That's the architecture I expect we'll see more of as AI becomes another interface for traditional web applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want to Add ChatGPT Actions to Laravel?
&lt;/h2&gt;

&lt;p&gt;If you have a Laravel or custom PHP application and want to explore secure ChatGPT integration, OAuth, APIs, or AI-driven workflows, visit:&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom PHP Design
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;https://www.customphpdesign.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We specialize in custom PHP and Laravel development, including integrations that connect existing applications with modern APIs, cloud infrastructure, and AI platforms.&lt;/p&gt;

&lt;p&gt;Instead of replacing years of existing development, we can help determine how to &lt;strong&gt;extend what you already have&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Laravel + APIs + OAuth + GPT Actions can turn an existing application into something your users can interact with conversationally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that's a pretty exciting direction for custom web development.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is based on lessons learned while implementing and debugging a real Laravel-to-ChatGPT OAuth integration. Always review the latest OpenAI GPT and Actions documentation before implementing your own integration, as account availability, publishing requirements, OAuth behavior, and platform features can change.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>ai</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>What It Really Takes to Get YouTube Live and TikTok Publishing Approved</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Thu, 13 Aug 2026 23:16:07 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/what-it-really-takes-to-get-youtube-live-and-tiktok-publishing-approved-en</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/what-it-really-takes-to-get-youtube-live-and-tiktok-publishing-approved-en</guid>
      <description>&lt;p&gt;Building a social media integration is one thing.&lt;/p&gt;

&lt;p&gt;Getting the social network to actually approve it for production is another.&lt;/p&gt;

&lt;p&gt;Over the past several months, I have been building &lt;a href="https://vidsyndicate.com" rel="noopener noreferrer"&gt;VidSyndicate&lt;/a&gt;, a social media publishing and video distribution platform. One of the goals behind the project is to give businesses a single application for managing content across networks such as Facebook, Instagram, LinkedIn, YouTube, Pinterest, TikTok, and others.&lt;/p&gt;

&lt;p&gt;From a development standpoint, connecting an API is often the easy part.&lt;/p&gt;

&lt;p&gt;The difficult part can be getting through the platform's review process.&lt;/p&gt;

&lt;p&gt;Recently, we reached two major milestones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;YouTube Live was approved&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TikTok's draft/inbox publishing workflow was approved&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither approval was a simple "submit the app and wait" process.&lt;/p&gt;

&lt;p&gt;There were multiple rounds of development, documentation, demonstrations, policy changes, scope explanations, branding updates, and reviewer feedback before we got there.&lt;/p&gt;

&lt;p&gt;As the developer behind both &lt;a href="https://www.customphpdesign.com" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt; and VidSyndicate, the experience reinforced something I have been seeing for years:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Social media API integration requires more than knowing how to make an HTTP request.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It requires understanding the platform, OAuth, permissions, security, application architecture, compliance, and increasingly, how to communicate your implementation to an auditor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building YouTube Live Into VidSyndicate
&lt;/h2&gt;

&lt;p&gt;The YouTube integration started with normal video publishing, but I wanted VidSyndicate to go considerably further.&lt;/p&gt;

&lt;p&gt;I wanted users to be able to create and manage a YouTube livestream directly through VidSyndicate.&lt;/p&gt;

&lt;p&gt;That meant integrating with Google's OAuth system, the YouTube Data API, and YouTube's live streaming functionality.&lt;/p&gt;

&lt;p&gt;The completed workflow allows VidSyndicate to handle much of the livestream lifecycle from inside the application.&lt;/p&gt;

&lt;p&gt;That includes creating the broadcast, creating and connecting the stream, monitoring its status, displaying live information, and ending the broadcast.&lt;/p&gt;

&lt;p&gt;Behind the scenes, the actual streaming infrastructure was another engineering project entirely.&lt;/p&gt;

&lt;p&gt;VidSyndicate uses cloud infrastructure and FFmpeg-based processing to handle the video stream. I eventually moved toward isolated AWS ECS tasks for livestream workloads rather than treating streaming like a normal web request.&lt;/p&gt;

&lt;p&gt;That part was challenging.&lt;/p&gt;

&lt;p&gt;But getting the OAuth permissions approved was a different kind of challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code Worked Before the Review Was Finished
&lt;/h2&gt;

&lt;p&gt;This is something businesses need to understand when developing social media integrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Working code does not automatically mean you have a production-ready integration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can successfully authenticate against an API.&lt;/p&gt;

&lt;p&gt;You can successfully create a broadcast.&lt;/p&gt;

&lt;p&gt;You can successfully upload a video.&lt;/p&gt;

&lt;p&gt;You can successfully receive data.&lt;/p&gt;

&lt;p&gt;And your application can still fail the platform review.&lt;/p&gt;

&lt;p&gt;Google needed to understand exactly why VidSyndicate requested its YouTube permissions and how those permissions were being used.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;youtube.upload&lt;/code&gt;, the purpose is fairly straightforward: users authorize VidSyndicate to upload videos they have selected.&lt;/p&gt;

&lt;p&gt;The live streaming functionality requires broader capabilities.&lt;/p&gt;

&lt;p&gt;VidSyndicate needs to create and manage broadcasts and streams, connect them, monitor their state, interact with live functionality, and manage the broadcast lifecycle.&lt;/p&gt;

&lt;p&gt;Explaining that clearly became part of getting the application approved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then Comes the Screencast
&lt;/h2&gt;

&lt;p&gt;A social platform reviewer doesn't have the same context the developer has.&lt;/p&gt;

&lt;p&gt;I had spent months working on VidSyndicate. I knew exactly what every button did and why every API request existed.&lt;/p&gt;

&lt;p&gt;The reviewer doesn't.&lt;/p&gt;

&lt;p&gt;That means the demonstration itself becomes part of the engineering process.&lt;/p&gt;

&lt;p&gt;For the YouTube review, we had to make sure the demonstration clearly showed the authorization process and the actual functionality associated with the requested permissions.&lt;/p&gt;

&lt;p&gt;The review material walked through the process from beginning to end:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect the YouTube account.&lt;/li&gt;
&lt;li&gt;Display the requested Google/YouTube permissions.&lt;/li&gt;
&lt;li&gt;Create a livestream.&lt;/li&gt;
&lt;li&gt;Start the streaming infrastructure.&lt;/li&gt;
&lt;li&gt;Verify the broadcast through YouTube.&lt;/li&gt;
&lt;li&gt;Monitor the live broadcast.&lt;/li&gt;
&lt;li&gt;Demonstrate the application's live functionality.&lt;/li&gt;
&lt;li&gt;End the stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The privacy policy also had to clearly explain how Google and YouTube user data was accessed and used.&lt;/p&gt;

&lt;p&gt;Even small details mattered.&lt;/p&gt;

&lt;p&gt;At one point, Google's feedback required the OAuth consent experience to make the services being requested clearer. That meant revisiting the consent flow and the supporting demonstration rather than simply saying, "The API works."&lt;/p&gt;

&lt;p&gt;Eventually the functionality, branding requirements, privacy disclosures, and requested scopes moved through the verification process.&lt;/p&gt;

&lt;h2&gt;
  
  
  TikTok Was Its Own Adventure
&lt;/h2&gt;

&lt;p&gt;TikTok presented a completely different review experience.&lt;/p&gt;

&lt;p&gt;VidSyndicate supports a TikTok workflow where a video can be sent to TikTok and delivered through TikTok's inbox/draft-style publishing experience.&lt;/p&gt;

&lt;p&gt;The user completes the final publishing process inside TikTok.&lt;/p&gt;

&lt;p&gt;From an application-design perspective, it is a useful middle ground.&lt;/p&gt;

&lt;p&gt;VidSyndicate can prepare and transfer the content while TikTok retains the final publishing interaction.&lt;/p&gt;

&lt;p&gt;Getting that functionality approved still required going back and forth through TikTok's review process multiple times.&lt;/p&gt;

&lt;p&gt;And this wasn't my first experience dealing with TikTok review.&lt;/p&gt;

&lt;p&gt;We had gone through TikTok audit attempts before. Each submission teaches you a little more about what the reviewer expects to see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sometimes the Rejection Isn't About Your API Code
&lt;/h2&gt;

&lt;p&gt;One of the TikTok review issues is a good example of why social integration work can become frustrating.&lt;/p&gt;

&lt;p&gt;The underlying integration could work correctly while something seemingly minor prevented approval.&lt;/p&gt;

&lt;p&gt;Branding was one of those issues.&lt;/p&gt;

&lt;p&gt;The application icon needed to properly match the product branding.&lt;/p&gt;

&lt;p&gt;That meant making sure the icon, application identity, and website presentation were consistent enough for the reviewer.&lt;/p&gt;

&lt;p&gt;From a programmer's perspective, changing an icon has absolutely nothing to do with whether an OAuth callback or publishing API works.&lt;/p&gt;

&lt;p&gt;From the platform's perspective, however, identity and branding are part of determining whether users understand which application they are authorizing.&lt;/p&gt;

&lt;p&gt;So you fix it.&lt;/p&gt;

&lt;p&gt;Then you submit again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submit, Wait, Fix, Demonstrate, Repeat
&lt;/h2&gt;

&lt;p&gt;That became a recurring pattern during development:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
Build the integration
        ↓
Test it
        ↓
Submit for review
        ↓
Wait
        ↓
Receive reviewer feedback
        ↓
Determine what the reviewer actually needs
        ↓
Update the application or documentation
        ↓
Record another demonstration when necessary
        ↓
Resubmit
        ↓
Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>socialmedia</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Laravel Development Process: From Idea to Production</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Wed, 12 Aug 2026 09:33:42 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/laravel-development-process-from-idea-to-production-219a</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/laravel-development-process-from-idea-to-production-219a</guid>
      <description>&lt;p&gt;Building a Laravel application involves much more than writing PHP code. A production application needs to solve a real business problem, handle users and data reliably, survive deployments, remain secure, and continue to be maintainable as requirements change.&lt;/p&gt;

&lt;p&gt;Laravel provides an excellent foundation for building modern web applications, but the framework is only one part of the development process.&lt;/p&gt;

&lt;p&gt;A successful Laravel project typically moves through several stages, from understanding the original business idea to deploying, monitoring, and improving the application in production.&lt;/p&gt;

&lt;p&gt;Here is what that process looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start With the Business Problem
&lt;/h2&gt;

&lt;p&gt;Before thinking about controllers, models, databases, or cloud infrastructure, the first step is understanding what the application actually needs to accomplish.&lt;/p&gt;

&lt;p&gt;A project might begin with a simple request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We need a customer portal."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a starting point, but it isn't a specification.&lt;/p&gt;

&lt;p&gt;What should customers be able to do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create and manage accounts?&lt;/li&gt;
&lt;li&gt;Upload documents?&lt;/li&gt;
&lt;li&gt;Manage subscriptions?&lt;/li&gt;
&lt;li&gt;Make payments?&lt;/li&gt;
&lt;li&gt;View reports?&lt;/li&gt;
&lt;li&gt;Communicate with employees?&lt;/li&gt;
&lt;li&gt;Receive notifications?&lt;/li&gt;
&lt;li&gt;Manage multiple users within an organization?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions start turning an idea into actual application requirements.&lt;/p&gt;

&lt;p&gt;One of the easiest ways for a software project to become unnecessarily expensive is to begin development before the problem has been clearly defined.&lt;/p&gt;

&lt;p&gt;Laravel can make development faster, but building the wrong application faster doesn't solve the underlying problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Define the MVP
&lt;/h2&gt;

&lt;p&gt;Once the requirements become clearer, the next step is determining what belongs in the first release.&lt;/p&gt;

&lt;p&gt;I generally separate features into two categories:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does the application need in order to provide value?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can be added later?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first category becomes the Minimum Viable Product, or MVP.&lt;/p&gt;

&lt;p&gt;For example, a new SaaS application might initially require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User registration&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Account management&lt;/li&gt;
&lt;li&gt;Subscription billing&lt;/li&gt;
&lt;li&gt;The application's primary feature&lt;/li&gt;
&lt;li&gt;Basic administration&lt;/li&gt;
&lt;li&gt;Email notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Features such as advanced analytics, AI integrations, extensive automation, or mobile applications may be valuable, but they don't necessarily need to be part of version one.&lt;/p&gt;

&lt;p&gt;Keeping the initial scope focused makes it easier to get a working application into production and start learning from real users.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Plan the Laravel Application Architecture
&lt;/h2&gt;

&lt;p&gt;Once the scope is understood, development can move into application architecture.&lt;/p&gt;

&lt;p&gt;Laravel provides many of the components needed for modern web applications, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Middleware&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Eloquent ORM&lt;/li&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;li&gt;Scheduled tasks&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;API resources&lt;/li&gt;
&lt;li&gt;Testing tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important architectural question isn't whether Laravel can perform these tasks.&lt;/p&gt;

&lt;p&gt;It's deciding how they should work together.&lt;/p&gt;

&lt;p&gt;A relatively small application may work perfectly with traditional Laravel controllers, models, services, jobs, and Blade templates.&lt;/p&gt;

&lt;p&gt;A larger application may need more separation between business logic and the HTTP layer.&lt;/p&gt;

&lt;p&gt;The goal shouldn't be to make the architecture as complicated as possible.&lt;/p&gt;

&lt;p&gt;The goal is to create an application structure that developers can understand and maintain as the software grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Design the Database Carefully
&lt;/h2&gt;

&lt;p&gt;Database design is one of the most important parts of custom application development.&lt;/p&gt;

&lt;p&gt;Laravel migrations make database changes easy to manage, while Eloquent makes interacting with relational data convenient.&lt;/p&gt;

&lt;p&gt;But neither eliminates the need for good database design.&lt;/p&gt;

&lt;p&gt;Before building features, think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relationships between entities&lt;/li&gt;
&lt;li&gt;Foreign keys&lt;/li&gt;
&lt;li&gt;Indexes&lt;/li&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Unique constraints&lt;/li&gt;
&lt;li&gt;Historical data&lt;/li&gt;
&lt;li&gt;Soft deletes&lt;/li&gt;
&lt;li&gt;Data retention&lt;/li&gt;
&lt;li&gt;Expected query patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Poor database decisions can be relatively invisible during early development and become painful once an application contains millions of records.&lt;/p&gt;

&lt;p&gt;Changing a database schema with 500 test records is easy.&lt;/p&gt;

&lt;p&gt;Changing it safely after the application has accumulated years of production data can be much more complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Build the Core Laravel Application
&lt;/h2&gt;

&lt;p&gt;With the architecture and data model established, development can move into the application's primary features.&lt;/p&gt;

&lt;p&gt;Laravel's conventions help developers move quickly.&lt;/p&gt;

&lt;p&gt;A typical feature might involve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating or updating a migration&lt;/li&gt;
&lt;li&gt;Creating the Eloquent model&lt;/li&gt;
&lt;li&gt;Defining relationships&lt;/li&gt;
&lt;li&gt;Creating routes&lt;/li&gt;
&lt;li&gt;Adding controllers or actions&lt;/li&gt;
&lt;li&gt;Implementing authorization&lt;/li&gt;
&lt;li&gt;Building validation&lt;/li&gt;
&lt;li&gt;Adding the user interface or API&lt;/li&gt;
&lt;li&gt;Writing tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More complex features may also involve Laravel jobs, queues, events, notifications, commands, or scheduled tasks.&lt;/p&gt;

&lt;p&gt;Keeping business logic organized becomes increasingly important as the application grows.&lt;/p&gt;

&lt;p&gt;A controller containing hundreds of lines of unrelated business logic may work today, but it becomes difficult to maintain later.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Add APIs and Third-Party Integrations
&lt;/h2&gt;

&lt;p&gt;Most modern applications don't operate in isolation.&lt;/p&gt;

&lt;p&gt;A Laravel application might integrate with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payment providers&lt;/li&gt;
&lt;li&gt;Email services&lt;/li&gt;
&lt;li&gt;Social platforms&lt;/li&gt;
&lt;li&gt;CRM systems&lt;/li&gt;
&lt;li&gt;Accounting software&lt;/li&gt;
&lt;li&gt;Cloud storage&lt;/li&gt;
&lt;li&gt;AI APIs&lt;/li&gt;
&lt;li&gt;Analytics platforms&lt;/li&gt;
&lt;li&gt;Internal business systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Laravel provides excellent tools for building APIs and communicating with external services.&lt;/p&gt;

&lt;p&gt;The harder part is designing integrations to handle failure.&lt;/p&gt;

&lt;p&gt;External APIs can time out.&lt;/p&gt;

&lt;p&gt;Rate limits can be reached.&lt;/p&gt;

&lt;p&gt;Credentials can expire.&lt;/p&gt;

&lt;p&gt;Providers can return unexpected responses.&lt;/p&gt;

&lt;p&gt;A production integration should account for those possibilities rather than assuming every API request will succeed.&lt;/p&gt;

&lt;p&gt;Queues and retry mechanisms can be particularly useful when an external operation doesn't need to complete during the user's original web request.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Use Queues for Background Processing
&lt;/h2&gt;

&lt;p&gt;Not every operation should happen while a user waits for a page to load.&lt;/p&gt;

&lt;p&gt;Laravel queues are useful for operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending emails&lt;/li&gt;
&lt;li&gt;Processing uploaded files&lt;/li&gt;
&lt;li&gt;Generating reports&lt;/li&gt;
&lt;li&gt;Calling external APIs&lt;/li&gt;
&lt;li&gt;Importing large datasets&lt;/li&gt;
&lt;li&gt;Processing videos&lt;/li&gt;
&lt;li&gt;Sending notifications&lt;/li&gt;
&lt;li&gt;Running AI operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of forcing the user to wait, the application can dispatch a job and allow a queue worker to process it separately.&lt;/p&gt;

&lt;p&gt;This can significantly improve application responsiveness and reliability.&lt;/p&gt;

&lt;p&gt;It also introduces another production consideration: queue workers need to be deployed, monitored, restarted when necessary, and scaled as workload increases.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Use Git From the Beginning
&lt;/h2&gt;

&lt;p&gt;Source control shouldn't be something added shortly before deployment.&lt;/p&gt;

&lt;p&gt;Git should be part of the development workflow from the beginning.&lt;/p&gt;

&lt;p&gt;A typical workflow might include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature branches&lt;/li&gt;
&lt;li&gt;Pull or merge requests&lt;/li&gt;
&lt;li&gt;Code review&lt;/li&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;Deployment from a controlled branch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even when one developer is building the application, source control provides a history of changes and a safer way to manage releases.&lt;/p&gt;

&lt;p&gt;Once multiple developers become involved, a consistent Git workflow becomes even more important.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Test Before Production
&lt;/h2&gt;

&lt;p&gt;Laravel includes strong support for automated testing.&lt;/p&gt;

&lt;p&gt;Tests can verify things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;API responses&lt;/li&gt;
&lt;li&gt;Database operations&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Business rules&lt;/li&gt;
&lt;li&gt;User workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not every application requires thousands of tests, but critical business functionality should have automated protection.&lt;/p&gt;

&lt;p&gt;Imagine an application where customers purchase subscriptions.&lt;/p&gt;

&lt;p&gt;A test could verify that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer selects a plan.&lt;/li&gt;
&lt;li&gt;The payment provider confirms the transaction.&lt;/li&gt;
&lt;li&gt;The subscription is created.&lt;/li&gt;
&lt;li&gt;The correct permissions are assigned.&lt;/li&gt;
&lt;li&gt;The customer receives confirmation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That test becomes especially valuable months later when another feature changes part of the billing system.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Prepare the Application for Production
&lt;/h2&gt;

&lt;p&gt;Development and production environments are different.&lt;/p&gt;

&lt;p&gt;Before deployment, several areas need attention.&lt;/p&gt;

&lt;p&gt;These may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Production database configuration&lt;/li&gt;
&lt;li&gt;Cache configuration&lt;/li&gt;
&lt;li&gt;Queue configuration&lt;/li&gt;
&lt;li&gt;Session storage&lt;/li&gt;
&lt;li&gt;File storage&lt;/li&gt;
&lt;li&gt;HTTPS&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;li&gt;Email configuration&lt;/li&gt;
&lt;li&gt;Security settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Laravel's configuration system makes managing environments straightforward, but production credentials should never be stored directly in source control.&lt;/p&gt;

&lt;p&gt;Secrets should be managed securely through the deployment platform or cloud infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Build CI/CD Instead of Deploying Manually
&lt;/h2&gt;

&lt;p&gt;Manual deployments may work initially, but they become risky as applications grow.&lt;/p&gt;

&lt;p&gt;A CI/CD pipeline can automate much of the release process.&lt;/p&gt;

&lt;p&gt;A Laravel deployment pipeline might:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve the application source&lt;/li&gt;
&lt;li&gt;Install Composer dependencies&lt;/li&gt;
&lt;li&gt;Install and build frontend dependencies&lt;/li&gt;
&lt;li&gt;Run automated tests&lt;/li&gt;
&lt;li&gt;Build a Docker image&lt;/li&gt;
&lt;li&gt;Push the image to a container registry&lt;/li&gt;
&lt;li&gt;Deploy the new application version&lt;/li&gt;
&lt;li&gt;Run database migrations&lt;/li&gt;
&lt;li&gt;Restart workers&lt;/li&gt;
&lt;li&gt;Verify application health&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Automation makes deployments more repeatable.&lt;/p&gt;

&lt;p&gt;If deploying an application requires someone to remember 15 commands in exactly the correct order, eventually something will be missed.&lt;/p&gt;

&lt;p&gt;The deployment process should be part of the application's architecture rather than an afterthought.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Deploy Laravel to Production Infrastructure
&lt;/h2&gt;

&lt;p&gt;Laravel can run in many environments.&lt;/p&gt;

&lt;p&gt;For smaller applications, a traditional Linux server may be perfectly appropriate.&lt;/p&gt;

&lt;p&gt;Larger applications may use cloud infrastructure with services for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compute&lt;/li&gt;
&lt;li&gt;Load balancing&lt;/li&gt;
&lt;li&gt;Managed databases&lt;/li&gt;
&lt;li&gt;Object storage&lt;/li&gt;
&lt;li&gt;Container orchestration&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Secrets management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS is one option frequently used for production Laravel applications.&lt;/p&gt;

&lt;p&gt;Depending on the application's requirements, an AWS architecture might include services such as EC2, ECS, ECR, RDS, S3, CloudWatch, Route 53, and load balancers.&lt;/p&gt;

&lt;p&gt;Infrastructure should match the application's actual needs.&lt;/p&gt;

&lt;p&gt;Not every Laravel application requires Kubernetes or an elaborate microservice architecture.&lt;/p&gt;

&lt;p&gt;Good architecture is often about choosing the simplest solution that can reliably meet the requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Monitor What Happens After Deployment
&lt;/h2&gt;

&lt;p&gt;Getting the application into production isn't the end of development.&lt;/p&gt;

&lt;p&gt;It's the beginning of operating the application.&lt;/p&gt;

&lt;p&gt;Production monitoring should help answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the application available?&lt;/li&gt;
&lt;li&gt;Are requests becoming slower?&lt;/li&gt;
&lt;li&gt;Are errors increasing?&lt;/li&gt;
&lt;li&gt;Are queue jobs failing?&lt;/li&gt;
&lt;li&gt;Is the database overloaded?&lt;/li&gt;
&lt;li&gt;Is storage approaching capacity?&lt;/li&gt;
&lt;li&gt;Did the latest deployment introduce a problem?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Application logs, infrastructure metrics, error tracking, and alerts provide visibility into what users are actually experiencing.&lt;/p&gt;

&lt;p&gt;Without monitoring, teams often discover problems when customers report them.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. Plan for Backups and Recovery
&lt;/h2&gt;

&lt;p&gt;Backups are easy to ignore until they're needed.&lt;/p&gt;

&lt;p&gt;A production application should have a documented strategy for protecting important data.&lt;/p&gt;

&lt;p&gt;That may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated database backups&lt;/li&gt;
&lt;li&gt;Object storage protection&lt;/li&gt;
&lt;li&gt;Backup retention policies&lt;/li&gt;
&lt;li&gt;Database snapshots&lt;/li&gt;
&lt;li&gt;Recovery procedures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having backups isn't enough.&lt;/p&gt;

&lt;p&gt;You also need to know that they can actually be restored.&lt;/p&gt;

&lt;p&gt;A backup strategy that has never been tested is still a risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  15. Continue Improving the Application
&lt;/h2&gt;

&lt;p&gt;Production applications rarely stay unchanged.&lt;/p&gt;

&lt;p&gt;Users request features.&lt;/p&gt;

&lt;p&gt;Business processes change.&lt;/p&gt;

&lt;p&gt;External APIs change.&lt;/p&gt;

&lt;p&gt;Security updates become available.&lt;/p&gt;

&lt;p&gt;Traffic increases.&lt;/p&gt;

&lt;p&gt;New integrations become necessary.&lt;/p&gt;

&lt;p&gt;This is why maintainability matters from the beginning.&lt;/p&gt;

&lt;p&gt;An application that was rushed into production with no architecture, testing, deployment automation, or documentation becomes increasingly expensive to change.&lt;/p&gt;

&lt;p&gt;A well-structured Laravel application provides a much better foundation for continued development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Laravel Development Is More Than Writing PHP
&lt;/h2&gt;

&lt;p&gt;Laravel makes building sophisticated PHP applications significantly easier, but the framework is only one part of delivering production software.&lt;/p&gt;

&lt;p&gt;A complete Laravel development process includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idea → Requirements → Architecture → Database Design → Development → Testing → CI/CD → Infrastructure → Deployment → Monitoring → Maintenance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each stage affects the stages that follow.&lt;/p&gt;

&lt;p&gt;Good requirements make architecture easier.&lt;/p&gt;

&lt;p&gt;Good architecture makes development easier.&lt;/p&gt;

&lt;p&gt;Testing makes deployments safer.&lt;/p&gt;

&lt;p&gt;Automation makes releases repeatable.&lt;/p&gt;

&lt;p&gt;Monitoring makes production problems easier to identify.&lt;/p&gt;

&lt;p&gt;And maintainable code makes the next feature easier to build.&lt;/p&gt;

&lt;p&gt;That's ultimately what separates simply building a Laravel application from building a Laravel application that can reliably support a real business.&lt;/p&gt;

&lt;p&gt;I work extensively with Laravel, custom PHP applications, AWS infrastructure, APIs, CI/CD, and application modernization through &lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're interested in more articles about Laravel development and building production PHP applications, you can also find them on the &lt;a href="https://www.customphpdesign.com/blog" rel="noopener noreferrer"&gt;Custom PHP Design blog&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Robert Saylor is a PHP/Laravel developer and DevOps engineer with experience building, modernizing, deploying, and operating production web applications. He writes about Laravel development, custom PHP applications, AWS, DevOps, CI/CD, SaaS development, and practical software architecture at &lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Robert is also the author of &lt;a href="https://www.amazon.com/Vibe-Coding-Modern-Developer-Production-Ready/dp/B0H9KCM5GM" rel="noopener noreferrer"&gt;&lt;em&gt;Vibe Coding: The Modern Developer&lt;/em&gt;&lt;/a&gt;, a practical guide to building production-ready applications with modern development workflows, Laravel, Git, CI/CD, and AWS.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Long Road to Meta App Approval: Why Building Real SaaS Products Isn't Easy</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Thu, 06 Aug 2026 10:26:44 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/the-long-road-to-meta-app-approval-why-building-real-saas-products-isnt-easy-ne0</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/the-long-road-to-meta-app-approval-why-building-real-saas-products-isnt-easy-ne0</guid>
      <description>&lt;p&gt;When people look at a polished SaaS application, it's easy to assume it was built over a weekend.&lt;/p&gt;

&lt;p&gt;A login page.&lt;/p&gt;

&lt;p&gt;A dashboard.&lt;/p&gt;

&lt;p&gt;A few API integrations.&lt;/p&gt;

&lt;p&gt;Simple, right?&lt;/p&gt;

&lt;p&gt;Not even close.&lt;/p&gt;

&lt;p&gt;Over the past several months I've been building &lt;strong&gt;&lt;a href="https://vidsyndicate.com/" rel="noopener noreferrer"&gt;VidSyndicate&lt;/a&gt;&lt;/strong&gt;, a platform that allows creators to upload a video once and publish it across multiple social media platforms.&lt;/p&gt;

&lt;p&gt;The coding was only half the challenge.&lt;/p&gt;

&lt;p&gt;The real battle was getting approved by Meta.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Approval Process Is Much More Than Writing Code
&lt;/h2&gt;

&lt;p&gt;Connecting to Facebook and Instagram isn't as simple as requesting an API key.&lt;/p&gt;

&lt;p&gt;Meta requires developers to prove that their application is legitimate before granting access to publishing features.&lt;/p&gt;

&lt;p&gt;That means documenting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every permission requested&lt;/li&gt;
&lt;li&gt;Every API call&lt;/li&gt;
&lt;li&gt;Exactly how user data is used&lt;/li&gt;
&lt;li&gt;Privacy policy updates&lt;/li&gt;
&lt;li&gt;Terms of Service&lt;/li&gt;
&lt;li&gt;Screen recordings&lt;/li&gt;
&lt;li&gt;End-to-end demonstrations&lt;/li&gt;
&lt;li&gt;Production-ready user flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every permission has to be justified.&lt;/p&gt;

&lt;p&gt;Every feature has to work exactly as described.&lt;/p&gt;




&lt;h2&gt;
  
  
  Small Mistakes Mean Rejection
&lt;/h2&gt;

&lt;p&gt;During development I had several review cycles that didn't go as planned.&lt;/p&gt;

&lt;p&gt;Sometimes it was because Meta wanted additional clarification.&lt;/p&gt;

&lt;p&gt;Other times it was because a workflow wasn't demonstrated exactly the way reviewers expected.&lt;/p&gt;

&lt;p&gt;The APIs worked.&lt;/p&gt;

&lt;p&gt;The software worked.&lt;/p&gt;

&lt;p&gt;But platform reviews have their own requirements that go far beyond software development.&lt;/p&gt;

&lt;p&gt;You have to think like both a developer and a compliance officer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Final Result
&lt;/h2&gt;

&lt;p&gt;After another review cycle, the application was finally approved.&lt;/p&gt;

&lt;p&gt;The latest approval included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ business_management&lt;/li&gt;
&lt;li&gt;✅ pages_manage_posts&lt;/li&gt;
&lt;li&gt;✅ pages_show_list&lt;/li&gt;
&lt;li&gt;✅ pages_read_engagement&lt;/li&gt;
&lt;li&gt;✅ instagram_basic&lt;/li&gt;
&lt;li&gt;✅ instagram_business_basic&lt;/li&gt;
&lt;li&gt;✅ instagram_content_publish&lt;/li&gt;
&lt;li&gt;✅ instagram_manage_messages&lt;/li&gt;
&lt;li&gt;✅ instagram_business_manage_messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means VidSyndicate can now publish directly to Facebook Pages and Instagram Business accounts through Meta's official APIs.&lt;/p&gt;

&lt;p&gt;That approval represents months of development, testing, documentation, and revisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Software Is Only Part of the Product
&lt;/h2&gt;

&lt;p&gt;One lesson that every SaaS founder eventually learns is this:&lt;/p&gt;

&lt;p&gt;Writing the application is often the easiest part.&lt;/p&gt;

&lt;p&gt;Building a production-ready platform means dealing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OAuth security&lt;/li&gt;
&lt;li&gt;Token management&lt;/li&gt;
&lt;li&gt;API rate limits&lt;/li&gt;
&lt;li&gt;Permission reviews&lt;/li&gt;
&lt;li&gt;Privacy compliance&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Infrastructure&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;User experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these features are flashy, but they're what transform a side project into a product businesses can trust.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experience Matters
&lt;/h2&gt;

&lt;p&gt;This experience reinforced something I've believed for years.&lt;/p&gt;

&lt;p&gt;There's a significant difference between writing code that works and building software that's ready for production.&lt;/p&gt;

&lt;p&gt;Production software must handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Third-party platform requirements&lt;/li&gt;
&lt;li&gt;Security best practices&lt;/li&gt;
&lt;li&gt;Long-term maintainability&lt;/li&gt;
&lt;li&gt;Infrastructure automation&lt;/li&gt;
&lt;li&gt;Compliance reviews&lt;/li&gt;
&lt;li&gt;Unexpected edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the same engineering principles I use every day through &lt;strong&gt;&lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;&lt;/strong&gt;, where I build custom PHP, Laravel, AWS, and DevOps solutions for businesses. Developing client software over the years has provided the experience needed to tackle complex platform integrations like Meta's review process, while building &lt;strong&gt;&lt;a href="https://vidsyndicate.com/" rel="noopener noreferrer"&gt;VidSyndicate&lt;/a&gt;&lt;/strong&gt; has allowed me to apply those same production-ready practices to a real-world SaaS platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next for VidSyndicate
&lt;/h2&gt;

&lt;p&gt;With Meta approval complete, development can now focus on building new features instead of waiting on platform access.&lt;/p&gt;

&lt;p&gt;Current supported platforms include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YouTube&lt;/li&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Instagram&lt;/li&gt;
&lt;li&gt;TikTok&lt;/li&gt;
&lt;li&gt;LinkedIn&lt;/li&gt;
&lt;li&gt;Pinterest&lt;/li&gt;
&lt;li&gt;Bluesky&lt;/li&gt;
&lt;li&gt;Mastodon&lt;/li&gt;
&lt;li&gt;X&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm also continuing work on live streaming, AI-assisted publishing, and additional automation tools to help creators distribute content more efficiently from a single dashboard.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;One of the biggest misconceptions in software development is that success comes from writing clever code.&lt;/p&gt;

&lt;p&gt;In reality, successful products are built by solving hundreds of small problems that users never see.&lt;/p&gt;

&lt;p&gt;Meta approval wasn't just another checkbox.&lt;/p&gt;

&lt;p&gt;It was proof that VidSyndicate meets the standards required to integrate with one of the largest social platforms in the world.&lt;/p&gt;

&lt;p&gt;Whether I'm building custom software for clients through &lt;strong&gt;&lt;a href="https://www.customphpdesign.com/" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;&lt;/strong&gt; or developing &lt;strong&gt;&lt;a href="https://vidsyndicate.com/" rel="noopener noreferrer"&gt;VidSyndicate&lt;/a&gt;&lt;/strong&gt;, the goal is the same: build secure, scalable, production-ready software that people can rely on.&lt;/p&gt;

&lt;p&gt;Sometimes the hardest part of software development isn't writing the code.&lt;/p&gt;

&lt;p&gt;It's proving that your software is ready for the real world.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>saas</category>
      <category>meta</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>Building Software That Solves Real Problems with Laravel, AI, and AWS</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Sat, 01 Aug 2026 19:47:08 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/building-software-that-solves-real-problems-with-laravel-ai-and-aws-16gc</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/building-software-that-solves-real-problems-with-laravel-ai-and-aws-16gc</guid>
      <description>&lt;p&gt;When people think about software development, they often focus on the technology stack. At &lt;strong&gt;Custom PHP Design&lt;/strong&gt;, we believe technology is only part of the equation. The real goal is solving business problems with software that is reliable, scalable, and built to last.&lt;/p&gt;

&lt;p&gt;Using Laravel, PHP, AWS, DevOps, and AI-assisted development, we've built applications that help creators, authors, businesses, and researchers work more efficiently. Here are a few of the projects we've been working on.&lt;/p&gt;

&lt;h2&gt;
  
  
  VidSyndicate
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://vidsyndicate.com" rel="noopener noreferrer"&gt;https://vidsyndicate.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of our newest projects is &lt;strong&gt;VidSyndicate&lt;/strong&gt;, a social media publishing platform built for creators, businesses, and marketing teams.&lt;/p&gt;

&lt;p&gt;Instead of manually uploading the same video or post to multiple social networks, users can prepare content once, customize it for each platform, and publish everywhere from a single dashboard.&lt;/p&gt;

&lt;p&gt;Current areas of development include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-assisted content generation&lt;/li&gt;
&lt;li&gt;Cross-platform scheduling&lt;/li&gt;
&lt;li&gt;Video publishing workflows&lt;/li&gt;
&lt;li&gt;Social media automation&lt;/li&gt;
&lt;li&gt;Creator productivity tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our goal is simple: help creators spend more time creating and less time copying and pasting content.&lt;/p&gt;




&lt;h2&gt;
  
  
  CoverStudioAI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://coverstudioai.com" rel="noopener noreferrer"&gt;https://coverstudioai.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creating professional book covers shouldn't require expensive desktop software or years of graphic design experience.&lt;/p&gt;

&lt;p&gt;That's why we built &lt;strong&gt;CoverStudioAI&lt;/strong&gt;, a browser-based design platform focused on Amazon KDP authors.&lt;/p&gt;

&lt;p&gt;The platform allows authors to create paperback, hardcover, and Kindle covers that meet Amazon's publishing specifications. AI-assisted design tools help speed up the creative process while giving users complete control over the final design.&lt;/p&gt;

&lt;p&gt;Whether you're publishing your first book or your hundredth, CoverStudioAI aims to make professional cover design accessible to everyone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Historical Text Archive
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://historicaltextarchive.org" rel="noopener noreferrer"&gt;https://historicaltextarchive.org&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not every project we build is a SaaS platform.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Historical Text Archive&lt;/strong&gt; is one of our longest-running projects and preserves thousands of historical articles, photographs, and primary source documents from around the world.&lt;/p&gt;

&lt;p&gt;We've modernized the platform with Laravel, improved search capabilities, implemented structured SEO data, enhanced performance, and continue expanding the archive so historical research remains freely available to students, educators, and history enthusiasts.&lt;/p&gt;

&lt;p&gt;Technology can preserve knowledge just as effectively as it creates new experiences.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vibe Coding as the Modern Developer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.customphpdesign.com/book/vibe-coding-as-the-modern-developer" rel="noopener noreferrer"&gt;https://www.customphpdesign.com/book/vibe-coding-as-the-modern-developer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As AI continues to transform software development, we've documented many of the workflows and techniques we've adopted in &lt;strong&gt;Vibe Coding as the Modern Developer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than focusing only on prompts, the book explores how experienced developers can combine AI with Laravel, Git, CI/CD, AWS, DevOps, testing, and sound software architecture to build production-ready applications. AI is a powerful development partner, but engineering experience is still what turns ideas into reliable software.&lt;/p&gt;




&lt;h2&gt;
  
  
  About Custom PHP Design
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.customphpdesign.com" rel="noopener noreferrer"&gt;https://www.customphpdesign.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Custom PHP Design specializes in building custom Laravel applications, SaaS platforms, APIs, cloud infrastructure, AI-assisted development workflows, and legacy application modernization.&lt;/p&gt;

&lt;p&gt;Every project we create starts with the same question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How can software make someone's job easier?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whether we're helping creators automate social media publishing, enabling authors to design professional book covers, or preserving historical knowledge for future generations, our mission remains the same—building software that solves real-world problems.&lt;/p&gt;

&lt;p&gt;If you're interested in Laravel, PHP, AWS, AI-assisted development, or modern software engineering, we'd love to connect with you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>socialmedia</category>
      <category>laravel</category>
      <category>aws</category>
    </item>
    <item>
      <title>I Built a Social Media Publishing Tool Because I Was Tired of Copy/Paste</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Sat, 01 Aug 2026 10:19:22 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/i-built-a-social-media-publishing-tool-because-i-was-tired-of-copypaste-3kbm</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/i-built-a-social-media-publishing-tool-because-i-was-tired-of-copypaste-3kbm</guid>
      <description>&lt;p&gt;If you've ever launched a side project, SaaS, YouTube channel, or blog, you've probably experienced this workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a post.&lt;/li&gt;
&lt;li&gt;Publish it to X.&lt;/li&gt;
&lt;li&gt;Open LinkedIn.&lt;/li&gt;
&lt;li&gt;Rewrite it.&lt;/li&gt;
&lt;li&gt;Open Facebook.&lt;/li&gt;
&lt;li&gt;Change the formatting.&lt;/li&gt;
&lt;li&gt;Upload it to Bluesky.&lt;/li&gt;
&lt;li&gt;Then Mastodon.&lt;/li&gt;
&lt;li&gt;Then Pinterest.&lt;/li&gt;
&lt;li&gt;Then YouTube.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You spend more time publishing than creating.&lt;/p&gt;

&lt;p&gt;As a full-time DevOps engineer and indie developer, I found myself spending hours every week repeating the same tasks. Every platform has different requirements, different APIs, different formatting, different media limits, and different audiences. The actual content wasn't the hard part—it was everything after clicking &lt;strong&gt;Publish&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's why I started building &lt;strong&gt;VidSyndicate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://vidsyndicate.com" rel="noopener noreferrer"&gt;https://vidsyndicate.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Most social media tools focus on scheduling.&lt;/p&gt;

&lt;p&gt;Scheduling is useful.&lt;/p&gt;

&lt;p&gt;But scheduling doesn't solve the real bottleneck.&lt;/p&gt;

&lt;p&gt;The real bottleneck is preparing content for every platform.&lt;/p&gt;

&lt;p&gt;Creators today aren't just posting to one network anymore. They're publishing to LinkedIn, X, Facebook, Bluesky, Mastodon, YouTube, TikTok, Instagram, Pinterest, and whatever comes next. Every platform has different limits, audiences, and best practices.&lt;/p&gt;

&lt;p&gt;Managing all of that manually quickly becomes a second full-time job.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Built VidSyndicate
&lt;/h2&gt;

&lt;p&gt;I wanted one place where I could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect all of my social accounts&lt;/li&gt;
&lt;li&gt;Upload a video or create a post once&lt;/li&gt;
&lt;li&gt;Generate AI-assisted versions for each platform&lt;/li&gt;
&lt;li&gt;Review everything before publishing&lt;/li&gt;
&lt;li&gt;Schedule future content&lt;/li&gt;
&lt;li&gt;Manage drafts&lt;/li&gt;
&lt;li&gt;Publish everywhere from one dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The emphasis is on &lt;strong&gt;assisted publishing&lt;/strong&gt;, not spam.&lt;/p&gt;

&lt;p&gt;A LinkedIn post shouldn't read like a Tweet.&lt;/p&gt;

&lt;p&gt;A YouTube description shouldn't be copied directly into TikTok.&lt;/p&gt;

&lt;p&gt;Automation should remove repetitive work—not authenticity.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Should Help, Not Replace You
&lt;/h2&gt;

&lt;p&gt;One thing I wanted to avoid was "click once and blast identical content everywhere."&lt;/p&gt;

&lt;p&gt;That usually creates poor engagement.&lt;/p&gt;

&lt;p&gt;Instead, AI should help adapt content for each platform while keeping the creator in control.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Longer professional copy for LinkedIn&lt;/li&gt;
&lt;li&gt;Short engaging captions for X&lt;/li&gt;
&lt;li&gt;Optimized hashtags for Instagram&lt;/li&gt;
&lt;li&gt;Better descriptions for YouTube&lt;/li&gt;
&lt;li&gt;Platform-specific formatting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The human still approves everything.&lt;/p&gt;

&lt;p&gt;AI simply removes the repetitive editing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building in Public
&lt;/h2&gt;

&lt;p&gt;One of the most rewarding parts of this project has been building it in public.&lt;/p&gt;

&lt;p&gt;I've been documenting everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI redesigns&lt;/li&gt;
&lt;li&gt;API integrations&lt;/li&gt;
&lt;li&gt;SEO improvements&lt;/li&gt;
&lt;li&gt;Infrastructure decisions&lt;/li&gt;
&lt;li&gt;Meta App Review process&lt;/li&gt;
&lt;li&gt;New AI features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those conversations have often been more valuable than simply announcing new releases.&lt;/p&gt;

&lt;p&gt;Developers appreciate seeing the journey—not just the destination.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Building software is rarely about writing code.&lt;/p&gt;

&lt;p&gt;It's usually about removing friction.&lt;/p&gt;

&lt;p&gt;The less time creators spend copying, pasting, resizing, and rewriting content...&lt;/p&gt;

&lt;p&gt;...the more time they have to actually create.&lt;/p&gt;

&lt;p&gt;That's the problem I'm trying to solve.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm continuing to add features every week.&lt;/p&gt;

&lt;p&gt;Some upcoming improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additional AI tools&lt;/li&gt;
&lt;li&gt;Better publishing workflows&lt;/li&gt;
&lt;li&gt;Enhanced analytics&lt;/li&gt;
&lt;li&gt;More social network integrations&lt;/li&gt;
&lt;li&gt;Improved onboarding&lt;/li&gt;
&lt;li&gt;Video management features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is evolving quickly thanks to feedback from early users.&lt;/p&gt;




&lt;h2&gt;
  
  
  I'd Love Your Feedback
&lt;/h2&gt;

&lt;p&gt;If you're a developer, indie hacker, marketer, or content creator, I'd love to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the most frustrating part of publishing content across multiple social platforms?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to follow the project—or even try it for free—you can find it here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://vidsyndicate.com" rel="noopener noreferrer"&gt;https://vidsyndicate.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feedback is always welcome.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;webdev&lt;/code&gt; &lt;code&gt;saas&lt;/code&gt; &lt;code&gt;buildinpublic&lt;/code&gt; &lt;code&gt;productivity&lt;/code&gt; &lt;code&gt;ai&lt;/code&gt;&lt;/p&gt;

</description>
      <category>socialmedia</category>
      <category>saas</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Recovering a Hacked WordPress Site and Rebuilding It with Laravel</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Wed, 03 Jun 2026 10:16:15 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/recovering-a-hacked-wordpress-site-and-rebuilding-it-with-laravel-2fbn</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/recovering-a-hacked-wordpress-site-and-rebuilding-it-with-laravel-2fbn</guid>
      <description>&lt;p&gt;Recently I was brought into a project involving a legacy WordPress website that had suffered a security incident. By the time I got access, parts of the site were no longer functioning correctly, content was missing, and the overall platform had become difficult to maintain.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://customphpdesign.com" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;, we frequently help businesses modernize aging websites and web applications, but this project presented a unique challenge: recovering years of content while rebuilding the platform from the ground up.&lt;/p&gt;

&lt;p&gt;The client wanted to preserve as much content as possible while moving to a more maintainable and secure platform.&lt;/p&gt;

&lt;p&gt;Rather than attempting another WordPress recovery, we decided to rebuild the site entirely using Laravel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assessing the Damage
&lt;/h2&gt;

&lt;p&gt;The first step was determining what could be recovered.&lt;/p&gt;

&lt;p&gt;While portions of the site's database were available, the content was incomplete. Some pages existed only in archived versions of the website, while others had been modified or removed over time.&lt;/p&gt;

&lt;p&gt;The challenge wasn't just rebuilding the application—it was reconstructing years of content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging the Wayback Machine
&lt;/h2&gt;

&lt;p&gt;One of the most valuable tools during the recovery process was the Wayback Machine.&lt;/p&gt;

&lt;p&gt;Archived snapshots allowed us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recover missing pages&lt;/li&gt;
&lt;li&gt;Verify historical content&lt;/li&gt;
&lt;li&gt;Rebuild navigation structures&lt;/li&gt;
&lt;li&gt;Restore articles that were no longer present in backups&lt;/li&gt;
&lt;li&gt;Cross-reference content against available database records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many developers think of the Wayback Machine as a research tool, but in this case it became an essential part of the disaster recovery process.&lt;/p&gt;

&lt;p&gt;Without those archived snapshots, a significant amount of content would have been permanently lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Chose Laravel
&lt;/h2&gt;

&lt;p&gt;Once we had a recovery strategy, the next decision was selecting a platform.&lt;/p&gt;

&lt;p&gt;We chose Laravel because it provided:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A modern application architecture&lt;/li&gt;
&lt;li&gt;Strong security defaults&lt;/li&gt;
&lt;li&gt;Flexible routing&lt;/li&gt;
&lt;li&gt;Better long-term maintainability&lt;/li&gt;
&lt;li&gt;Full control over the data model&lt;/li&gt;
&lt;li&gt;Simplified deployment workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of fitting the project back into WordPress, Laravel allowed us to build exactly what the client needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Migration Process
&lt;/h2&gt;

&lt;p&gt;The migration involved more than simply importing content.&lt;/p&gt;

&lt;p&gt;We had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract recovered content&lt;/li&gt;
&lt;li&gt;Clean legacy HTML&lt;/li&gt;
&lt;li&gt;Normalize URLs&lt;/li&gt;
&lt;li&gt;Rebuild internal links&lt;/li&gt;
&lt;li&gt;Restore media references&lt;/li&gt;
&lt;li&gt;Generate SEO-friendly slugs&lt;/li&gt;
&lt;li&gt;Validate imported records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To speed up the process, we built custom import scripts that transformed recovered content into Laravel models.&lt;/p&gt;

&lt;p&gt;Automation handled the repetitive work while manual review ensured content quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preserving Search Visibility
&lt;/h2&gt;

&lt;p&gt;A major concern during any rebuild is SEO.&lt;/p&gt;

&lt;p&gt;To minimize disruption, we focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preserving URL structures where possible&lt;/li&gt;
&lt;li&gt;Implementing redirects&lt;/li&gt;
&lt;li&gt;Generating XML sitemaps&lt;/li&gt;
&lt;li&gt;Adding canonical URLs&lt;/li&gt;
&lt;li&gt;Creating structured data&lt;/li&gt;
&lt;li&gt;Maintaining internal linking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By treating SEO as part of the migration rather than an afterthought, we were able to launch the new platform without sacrificing discoverability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;A few things stood out during this project:&lt;/p&gt;

&lt;h3&gt;
  
  
  Backups Aren't Always Enough
&lt;/h3&gt;

&lt;p&gt;Many organizations assume they can recover quickly from backups, but gaps often exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Archive Services Can Be Invaluable
&lt;/h3&gt;

&lt;p&gt;The Wayback Machine provided access to content that was unavailable elsewhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebuilding Can Be More Efficient Than Repairing
&lt;/h3&gt;

&lt;p&gt;In some situations, continuing to patch a heavily customized legacy system costs more than rebuilding on a modern framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security and Maintainability Matter
&lt;/h3&gt;

&lt;p&gt;Moving to Laravel gave the client a platform that was easier to secure, update, and extend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern Frameworks Provide Long-Term Flexibility
&lt;/h3&gt;

&lt;p&gt;By rebuilding the application in Laravel, the client now has a platform that can continue to evolve as business requirements change without being constrained by legacy plugins or themes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This project started as a recovery effort but became an opportunity to modernize an aging platform.&lt;/p&gt;

&lt;p&gt;By combining archived content from the Wayback Machine with custom migration tooling and a Laravel rebuild, we were able to preserve years of valuable content while delivering a faster, more maintainable application.&lt;/p&gt;

&lt;p&gt;Sometimes the best recovery plan isn't restoring what existed before—it's building something better.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://customphpdesign.com" rel="noopener noreferrer"&gt;Custom PHP Design&lt;/a&gt;, we specialize in Laravel development, legacy PHP modernization, AWS infrastructure, and custom web application development. Projects like this demonstrate how the right recovery strategy can turn a difficult situation into an opportunity to build a stronger, more secure platform for the future.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>wordpress</category>
      <category>webdev</category>
      <category>php</category>
    </item>
    <item>
      <title>Modernizing a 10+ Year Old PHP Application into Laravel 12 (Historical Text Archive Case Study)</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Sun, 29 Mar 2026 23:14:13 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/modernizing-a-10-year-old-php-application-into-laravel-12-historical-text-archive-case-study-9b2</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/modernizing-a-10-year-old-php-application-into-laravel-12-historical-text-archive-case-study-9b2</guid>
      <description>&lt;p&gt;When you inherit a PHP application that has been running for over a decade, stability and technical debt often grow together.&lt;/p&gt;

&lt;p&gt;That was the situation with the Historical Text Archive (HTA), a long-running content site containing hundreds of historical articles, primary sources, and educational material.&lt;/p&gt;

&lt;p&gt;Over time, the original codebase became harder to maintain, harder to extend, and increasingly difficult to integrate with modern tooling.&lt;/p&gt;

&lt;p&gt;Rather than continue patching the legacy system, we decided to modernize the platform using Laravel 12 while preserving the data structure and URLs that search engines already trust.&lt;/p&gt;

&lt;p&gt;HTA is publicly available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://historicaltextarchive.org" rel="noopener noreferrer"&gt;https://historicaltextarchive.org&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Goals of the Migration
&lt;/h2&gt;

&lt;p&gt;We wanted to improve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maintainability&lt;/li&gt;
&lt;li&gt;security&lt;/li&gt;
&lt;li&gt;search functionality&lt;/li&gt;
&lt;li&gt;extensibility&lt;/li&gt;
&lt;li&gt;performance&lt;/li&gt;
&lt;li&gt;developer experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;while keeping:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;existing URLs intact&lt;/li&gt;
&lt;li&gt;article IDs stable&lt;/li&gt;
&lt;li&gt;database structure understandable&lt;/li&gt;
&lt;li&gt;SEO authority preserved&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Challenges of Legacy PHP
&lt;/h2&gt;

&lt;p&gt;The original application was built more than 10 years ago using procedural PHP and a MySQL database.&lt;/p&gt;

&lt;p&gt;Common issues we encountered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mixed business logic and presentation&lt;/li&gt;
&lt;li&gt;limited separation of concerns&lt;/li&gt;
&lt;li&gt;manual SQL queries everywhere&lt;/li&gt;
&lt;li&gt;inconsistent naming conventions&lt;/li&gt;
&lt;li&gt;limited testability&lt;/li&gt;
&lt;li&gt;difficulty adding new features&lt;/li&gt;
&lt;li&gt;older authentication approach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite these limitations, the schema itself was solid. The database structure had aged well, making it a good candidate for migration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Migration Strategy
&lt;/h2&gt;

&lt;p&gt;Instead of rewriting everything at once, we focused on incremental modernization.&lt;/p&gt;

&lt;p&gt;High-level approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;export existing database schema&lt;/li&gt;
&lt;li&gt;generate Laravel migrations from schema&lt;/li&gt;
&lt;li&gt;generate Eloquent models&lt;/li&gt;
&lt;li&gt;replicate core functionality&lt;/li&gt;
&lt;li&gt;preserve routes and URLs&lt;/li&gt;
&lt;li&gt;add modern features&lt;/li&gt;
&lt;li&gt;improve search capabilities&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach reduced risk and allowed the application to remain functional during development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Converting SQL Schema into Laravel Migrations
&lt;/h2&gt;

&lt;p&gt;We started with a schema.sql dump from the legacy application.&lt;/p&gt;

&lt;p&gt;From there we generated Laravel migration files that recreated the structure in a fresh database.&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;version controlled schema&lt;/li&gt;
&lt;li&gt;easier onboarding for future developers&lt;/li&gt;
&lt;li&gt;consistent database structure across environments&lt;/li&gt;
&lt;li&gt;rollback capability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'articles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;increments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'artid'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'secid'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Generating Eloquent Models
&lt;/h2&gt;

&lt;p&gt;Once migrations were in place, we created Eloquent models for each major table.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$primaryKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'artid'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'secid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'body'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Section&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'secid'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structured ORM relationships&lt;/li&gt;
&lt;li&gt;simplified queries&lt;/li&gt;
&lt;li&gt;reusable logic&lt;/li&gt;
&lt;li&gt;easier refactoring&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Preserving SEO-Friendly URLs
&lt;/h2&gt;

&lt;p&gt;One of the most important requirements was preserving search engine rankings.&lt;/p&gt;

&lt;p&gt;Many HTA articles have been indexed for years.&lt;/p&gt;

&lt;p&gt;Breaking URLs would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reduce search visibility&lt;/li&gt;
&lt;li&gt;lose backlinks&lt;/li&gt;
&lt;li&gt;create poor user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We maintained existing routes wherever possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/articles/{artid}/{slug?}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ArticleController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'show'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allowed legacy URLs to continue working without redirects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Improving Search
&lt;/h2&gt;

&lt;p&gt;Search is central to any archive.&lt;/p&gt;

&lt;p&gt;The legacy system used simple SQL queries which worked but were limited.&lt;/p&gt;

&lt;p&gt;Laravel makes it easier to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expand search fields&lt;/li&gt;
&lt;li&gt;add filters&lt;/li&gt;
&lt;li&gt;build indexing logic&lt;/li&gt;
&lt;li&gt;support future AI categorization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example search query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'like'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'like'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Future improvements may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;full text indexing&lt;/li&gt;
&lt;li&gt;semantic search&lt;/li&gt;
&lt;li&gt;tagging automation&lt;/li&gt;
&lt;li&gt;historical topic clustering&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Modern Development Workflow
&lt;/h2&gt;

&lt;p&gt;Laravel provides a modern toolkit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrations&lt;/li&gt;
&lt;li&gt;artisan CLI&lt;/li&gt;
&lt;li&gt;blade templates&lt;/li&gt;
&lt;li&gt;validation rules&lt;/li&gt;
&lt;li&gt;routing system&lt;/li&gt;
&lt;li&gt;middleware&lt;/li&gt;
&lt;li&gt;queue support&lt;/li&gt;
&lt;li&gt;testing tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example artisan usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan migrate
php artisan make:model Article
php artisan make:controller ArticleController
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Development is now faster and more consistent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Preparing for Future Features
&lt;/h2&gt;

&lt;p&gt;With Laravel in place, HTA can now expand into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;newsletter automation&lt;/li&gt;
&lt;li&gt;"On this day in history" article discovery&lt;/li&gt;
&lt;li&gt;improved article categorization&lt;/li&gt;
&lt;li&gt;editorial workflow improvements&lt;/li&gt;
&lt;li&gt;API endpoints&lt;/li&gt;
&lt;li&gt;AI assisted tagging&lt;/li&gt;
&lt;li&gt;advanced search capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the foundation is now structured, new features are significantly easier to implement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Migrating a legacy PHP application does not require abandoning everything.&lt;/p&gt;

&lt;p&gt;In many cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the database design is still valuable&lt;/li&gt;
&lt;li&gt;the content is still valuable&lt;/li&gt;
&lt;li&gt;the domain authority is still valuable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Laravel allows modernization without losing what already works.&lt;/p&gt;

&lt;p&gt;For HTA, the result is a platform that keeps its historical depth while gaining modern flexibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Live Site
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://historicaltextarchive.org" rel="noopener noreferrer"&gt;https://historicaltextarchive.org&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you are working with a legacy PHP application, start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;database migration&lt;/li&gt;
&lt;li&gt;model structure&lt;/li&gt;
&lt;li&gt;route preservation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modernization can happen incrementally without disrupting users.&lt;/p&gt;

&lt;p&gt;Sometimes the best upgrade is not replacing everything, but building a better foundation underneath what already exists.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>webdev</category>
      <category>history</category>
    </item>
    <item>
      <title>VidShare Just Got Approved for Facebook, Instagram, and TikTok 🎉</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Fri, 27 Mar 2026 16:10:28 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/vidshare-just-got-approved-for-facebook-instagram-and-tiktok-511k</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/vidshare-just-got-approved-for-facebook-instagram-and-tiktok-511k</guid>
      <description>&lt;p&gt;This week was a big milestone for &lt;strong&gt;VidShare&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After weeks of development, testing, and API reviews, &lt;strong&gt;&lt;a href="https://vidshare.us" rel="noopener noreferrer"&gt;VidShare&lt;/a&gt; is now approved to publish video content to Facebook, Instagram, and TikTok&lt;/strong&gt;, bringing us closer to the goal of making video distribution simple again.&lt;/p&gt;

&lt;p&gt;VidShare started with a simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Upload once, shape the post, and publish across your video stack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Creators shouldn’t have to upload the same video 5+ times just to stay consistent online.&lt;/p&gt;

&lt;p&gt;And now, that workflow is finally becoming reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Publishing video content across multiple platforms is still surprisingly manual.&lt;/p&gt;

&lt;p&gt;Even experienced creators often:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export the same video multiple times&lt;/li&gt;
&lt;li&gt;Resize or tweak captions per platform&lt;/li&gt;
&lt;li&gt;Upload separately to each social network&lt;/li&gt;
&lt;li&gt;Try to keep everything scheduled consistently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This repetitive work slows creators down and makes consistency harder than it should be.&lt;/p&gt;

&lt;p&gt;Social media scheduling tools have existed for years, helping marketers plan and publish content more efficiently across channels.&lt;/p&gt;

&lt;p&gt;VidShare focuses specifically on video-first creators, simplifying the process of distributing video content across multiple platforms from one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Newly Approved Platforms
&lt;/h2&gt;

&lt;p&gt;This week, VidShare received approval for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook video publishing&lt;/li&gt;
&lt;li&gt;Instagram video publishing&lt;/li&gt;
&lt;li&gt;TikTok draft publishing&lt;/li&gt;
&lt;li&gt;LinkedIn video publishing (already available)&lt;/li&gt;
&lt;li&gt;YouTube publishing&lt;/li&gt;
&lt;li&gt;Pinterest video support&lt;/li&gt;
&lt;li&gt;Additional platforms including Bluesky and Mastodon&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these approvals, creators can now manage most of their core video distribution workflow from a single dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Workflow Inside VidShare
&lt;/h2&gt;

&lt;p&gt;The workflow is intentionally simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload your video once&lt;/li&gt;
&lt;li&gt;Customize captions for each platform if needed&lt;/li&gt;
&lt;li&gt;Choose publish time or schedule&lt;/li&gt;
&lt;li&gt;VidShare distributes the content automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No more jumping between apps just to post the same content repeatedly.&lt;/p&gt;

&lt;h2&gt;
  
  
  TikTok Draft Support
&lt;/h2&gt;

&lt;p&gt;One of the most requested features has been TikTok support.&lt;/p&gt;

&lt;p&gt;TikTok’s API review process is known to be more restrictive than other platforms, particularly around content publishing permissions.&lt;/p&gt;

&lt;p&gt;Instead of immediately publishing videos publicly, VidShare currently supports sending videos directly to the user's TikTok drafts inbox, giving creators full control before posting.&lt;/p&gt;

&lt;p&gt;This provides a balance between automation and creative flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Built VidShare
&lt;/h2&gt;

&lt;p&gt;VidShare was created after experiencing the same frustration many creators have:&lt;/p&gt;

&lt;p&gt;Uploading the same video repeatedly across platforms wastes time that could be spent creating better content.&lt;/p&gt;

&lt;p&gt;Many existing tools focus heavily on analytics or enterprise workflows.&lt;/p&gt;

&lt;p&gt;VidShare focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simplicity&lt;/li&gt;
&lt;li&gt;speed&lt;/li&gt;
&lt;li&gt;creator-first workflow&lt;/li&gt;
&lt;li&gt;AI-assisted optimization&lt;/li&gt;
&lt;li&gt;reducing repetitive tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to replace creativity, but to remove friction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;Now that major platform approvals are in place, the roadmap becomes clearer.&lt;/p&gt;

&lt;p&gt;Upcoming areas of exploration include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;improved scheduling workflows&lt;/li&gt;
&lt;li&gt;bulk upload capabilities&lt;/li&gt;
&lt;li&gt;expanded AI optimization tools&lt;/li&gt;
&lt;li&gt;deeper integrations with additional platforms&lt;/li&gt;
&lt;li&gt;improved creator discovery features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One step at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build in Public
&lt;/h2&gt;

&lt;p&gt;VidShare is still early, but momentum is building quickly.&lt;/p&gt;

&lt;p&gt;Recent milestones include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-platform publishing support&lt;/li&gt;
&lt;li&gt;AI-assisted captions and descriptions&lt;/li&gt;
&lt;li&gt;unified video calendar&lt;/li&gt;
&lt;li&gt;OAuth integrations across major platforms&lt;/li&gt;
&lt;li&gt;creator-focused workflow design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now:&lt;/p&gt;

&lt;p&gt;Facebook, Instagram, and TikTok approval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try VidShare
&lt;/h2&gt;

&lt;p&gt;If you create video content and want to simplify distribution:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vidshare.us" rel="noopener noreferrer"&gt;VidShare&lt;/a&gt; is designed to help you stay consistent without increasing workload.&lt;/p&gt;

&lt;p&gt;Upload once. Publish everywhere.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>news</category>
      <category>automation</category>
      <category>startup</category>
    </item>
    <item>
      <title>Introducing Get Discovered: A New Way for Creators to Grow with VidShare</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Thu, 19 Mar 2026 12:15:49 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/introducing-get-discovered-a-new-way-for-creators-to-grow-with-vidshare-i58</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/introducing-get-discovered-a-new-way-for-creators-to-grow-with-vidshare-i58</guid>
      <description>&lt;p&gt;If you’ve ever uploaded content and felt like it just… disappeared into the void—you’re not alone.&lt;/p&gt;

&lt;p&gt;One of the hardest parts of being a creator today isn’t making content.&lt;/p&gt;

&lt;p&gt;It’s getting noticed.&lt;/p&gt;

&lt;p&gt;That’s exactly why we built something new inside VidShare:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👉 Get Discovered&lt;/strong&gt;*&lt;br&gt;
&lt;a href="https://vidshare.us/get-discovered" rel="noopener noreferrer"&gt;https://vidshare.us/get-discovered&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 The Problem
&lt;/h2&gt;

&lt;p&gt;Creators today are everywhere—YouTube, Instagram, TikTok, Pinterest… the list never ends.&lt;/p&gt;

&lt;p&gt;Tools like VidShare already help solve one big issue:&lt;br&gt;
upload once, distribute everywhere.&lt;/p&gt;

&lt;p&gt;But distribution alone doesn’t solve visibility.&lt;/p&gt;

&lt;p&gt;Because even if your content is everywhere…&lt;br&gt;
it still has to compete with everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 The Idea Behind Get Discovered
&lt;/h2&gt;

&lt;p&gt;We wanted to flip the script.&lt;/p&gt;

&lt;p&gt;Instead of chasing algorithms, what if creators had a dedicated space to be seen?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get Discovered&lt;/strong&gt; is exactly that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A place where creators can feature themselves&lt;/li&gt;
&lt;li&gt;A system where visibility is earned through engagement&lt;/li&gt;
&lt;li&gt;A platform designed to help creators grow their audience organically&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔥 How It Works
&lt;/h2&gt;

&lt;p&gt;Here’s the core concept:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Creators Sign Up (Free)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Anyone can create an account and join the discovery system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Get Featured&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creators are scheduled into a featured window where they get visibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Community Voting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During that time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users can upvote creators&lt;/li&gt;
&lt;li&gt;The most engaging creators rise to the top&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Follow &amp;amp; Grow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Visitors can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click into creator profiles&lt;/li&gt;
&lt;li&gt;Follow them on their platforms&lt;/li&gt;
&lt;li&gt;Discover new content worth watching&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧠 Why This Matters
&lt;/h2&gt;

&lt;p&gt;Most platforms are built around algorithms.&lt;/p&gt;

&lt;p&gt;Get Discovered is built around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;People&lt;/li&gt;
&lt;li&gt;Community&lt;/li&gt;
&lt;li&gt;Fair exposure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It creates a level playing field where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New creators can compete&lt;/li&gt;
&lt;li&gt;Small creators can be seen&lt;/li&gt;
&lt;li&gt;Great content actually has a chance to win&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔗 Built to Work With Your Existing Content
&lt;/h2&gt;

&lt;p&gt;This isn’t another platform you have to “start over” on.&lt;/p&gt;

&lt;p&gt;It works alongside everything you’re already doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload once with VidShare&lt;/li&gt;
&lt;li&gt;Distribute everywhere&lt;/li&gt;
&lt;li&gt;Then use Get Discovered to drive attention back to you&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 What’s Next
&lt;/h2&gt;

&lt;p&gt;This is just the beginning.&lt;/p&gt;

&lt;p&gt;We’re exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leaderboards&lt;/li&gt;
&lt;li&gt;Creator tiers&lt;/li&gt;
&lt;li&gt;Featured categories&lt;/li&gt;
&lt;li&gt;Deeper analytics&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👉 Try It Out
&lt;/h2&gt;

&lt;p&gt;If you’re a content creator looking to grow your audience:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check it out here:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://vidshare.us/get-discovered" rel="noopener noreferrer"&gt;https://vidshare.us/get-discovered&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Build a Tool to Stop Uploading the Same Video 5 Times</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Wed, 18 Mar 2026 11:01:24 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/i-build-a-tool-to-stop-uploading-the-same-video-5-times-1341</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/i-build-a-tool-to-stop-uploading-the-same-video-5-times-1341</guid>
      <description>&lt;p&gt;As a developer and a content creator, I ran into a problem that felt way more painful than it should be:&lt;/p&gt;

&lt;p&gt;Uploading the same video… over and over… to different platforms.&lt;/p&gt;

&lt;p&gt;My workflow looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload to YouTube&lt;/li&gt;
&lt;li&gt;Upload again to Pinterest&lt;/li&gt;
&lt;li&gt;Upload again to Twitch&lt;/li&gt;
&lt;li&gt;Upload again to Mastodon&lt;/li&gt;
&lt;li&gt;Upload again to Bluesky&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every. Single. Time.&lt;/p&gt;

&lt;p&gt;It wasn’t just repetitive — it made it harder to stay consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Problem Isn’t Uploading — It’s Friction
&lt;/h2&gt;

&lt;p&gt;At first, I thought the issue was just “too many platforms.”&lt;/p&gt;

&lt;p&gt;But after doing this repeatedly, I realized something deeper:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The real problem is friction between platforms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;has its own upload flow&lt;/li&gt;
&lt;li&gt;its own UI&lt;/li&gt;
&lt;li&gt;its own quirks&lt;/li&gt;
&lt;li&gt;its own timing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even small differences add up.&lt;/p&gt;

&lt;p&gt;And when posting becomes annoying, consistency drops.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built (For Myself First)
&lt;/h2&gt;

&lt;p&gt;So I built a tool called VidShare.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Upload once → distribute everywhere.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of repeating the same process 5 times, I wanted a single workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload video once&lt;/li&gt;
&lt;li&gt;Select platforms&lt;/li&gt;
&lt;li&gt;Let the system handle the rest&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Works Today
&lt;/h2&gt;

&lt;p&gt;Right now, VidShare supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YouTube&lt;/li&gt;
&lt;li&gt;Pinterest&lt;/li&gt;
&lt;li&gt;Twitch&lt;/li&gt;
&lt;li&gt;Mastodon&lt;/li&gt;
&lt;li&gt;Bluesky&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some platforms were easy.&lt;/p&gt;

&lt;p&gt;Others… not so much.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardest Part Wasn’t the Code
&lt;/h2&gt;

&lt;p&gt;Surprisingly, building the system wasn’t the hardest part.&lt;/p&gt;

&lt;p&gt;Getting &lt;strong&gt;platform approvals&lt;/strong&gt; was.&lt;/p&gt;

&lt;p&gt;If you’ve ever worked with APIs like Meta or TikTok, you already know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strict permission reviews&lt;/li&gt;
&lt;li&gt;unclear requirements&lt;/li&gt;
&lt;li&gt;multiple submission cycles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At one point, I had to build an entire UI just to prove I was using a permission correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned Building This
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Repetition is a product opportunity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If something feels annoying every time you do it, there’s probably a product hiding there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. APIs are the real bottleneck&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can build fast — but approvals slow everything down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Consistency beats perfection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Making it easier to post consistently is more valuable than optimizing each post individually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Is Going
&lt;/h2&gt;

&lt;p&gt;I’m currently working through approvals for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Instagram&lt;/li&gt;
&lt;li&gt;TikTok&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those are in, the workflow becomes much more powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Most creators don’t struggle with making content.&lt;/p&gt;

&lt;p&gt;They struggle with distributing it efficiently.&lt;/p&gt;

&lt;p&gt;That’s the problem I’m trying to solve.&lt;/p&gt;

&lt;p&gt;If you're dealing with the same issue, I’d love to hear how you're handling multi-platform posting.&lt;/p&gt;

&lt;p&gt;Are you doing it manually, or using tools to streamline it?&lt;/p&gt;

&lt;p&gt;If you're dealing with the same problem, you can check it out here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://vidshare.us" rel="noopener noreferrer"&gt;https://vidshare.us&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love to hear how you're handling multi-platform posting.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>I got tired of uploading videos to multiple platforms, so I built this</title>
      <dc:creator>Robert Saylor</dc:creator>
      <pubDate>Tue, 17 Mar 2026 12:33:53 +0000</pubDate>
      <link>https://dev.to/robert_saylor_c9db74549e2/i-got-tired-of-uploading-videos-to-multiple-platforms-so-i-built-this-3a30</link>
      <guid>https://dev.to/robert_saylor_c9db74549e2/i-got-tired-of-uploading-videos-to-multiple-platforms-so-i-built-this-3a30</guid>
      <description>&lt;p&gt;If you create video content, you probably know this workflow:&lt;/p&gt;

&lt;p&gt;Record or edit a video&lt;br&gt;&lt;br&gt;
Upload to YouTube&lt;br&gt;&lt;br&gt;
Then upload the same video to TikTok&lt;br&gt;&lt;br&gt;
Then Instagram&lt;br&gt;&lt;br&gt;
Then maybe Pinterest or others  &lt;/p&gt;

&lt;p&gt;Each platform has its own process, formatting, scheduling, and quirks.&lt;/p&gt;

&lt;p&gt;What should be simple turns into a repetitive, time-consuming task.&lt;/p&gt;

&lt;p&gt;I ran into this myself after growing a small audience on TikTok. When I started posting to YouTube and other platforms, I realized I was spending more time uploading and managing posts than actually creating content.&lt;/p&gt;

&lt;p&gt;And it only gets worse as you scale.&lt;/p&gt;

&lt;p&gt;The problem isn’t creating content anymore — it’s distributing it.&lt;/p&gt;

&lt;p&gt;So I built a tool to solve that.&lt;/p&gt;

&lt;p&gt;VidShare is a platform that lets you upload your video once and distribute it across multiple platforms without repeating the same steps over and over.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
Upload → configure → post → repeat&lt;/p&gt;

&lt;p&gt;It becomes:&lt;br&gt;
Upload once → select platforms → schedule → done&lt;/p&gt;

&lt;p&gt;The goal isn’t to replace any platform, but to remove the friction between them.&lt;/p&gt;

&lt;p&gt;Right now, creators are stitching together workflows manually, even though the content is often the same across platforms.&lt;/p&gt;

&lt;p&gt;It feels like something that should have been solved already.&lt;/p&gt;

&lt;p&gt;I’m still early in building this, and currently looking for creators who want to test it and give feedback.&lt;/p&gt;

&lt;p&gt;If you’re dealing with the same problem, I’d genuinely like to hear how you’re handling it today.&lt;/p&gt;

&lt;p&gt;Also curious — do you think this is something platforms will eventually solve themselves, or will third-party tools always fill the gap?&lt;/p&gt;

&lt;p&gt;If you want to check it out or try it:&lt;br&gt;
&lt;a href="https://vidshare.us" rel="noopener noreferrer"&gt;https://vidshare.us&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
