Social Publishing

Connect your social accounts, write a post once, and publish it to one or more platforms — immediately or at a scheduled time. Everything is stored in your tenant database, and scheduled posts are published by a background worker.

Enabling the feature

Social Publishing is controlled by the SocialPosting feature toggle, which is on by default. Administrators can disable it under Settings → Feature Toggles.

Licence: publishing requires an active Social Posting licence. Claim your code under Settings → License Limits. The licence is separate from the toggle: the toggle exposes the page, the licence authorises publishing.

Connecting your accounts

Each user connects their own social accounts. Your administrator enables the providers once (see For administrators); after that:

  1. Open Social → Connections.
  2. Choose a provider and click Connect.
  3. Sign in with your own account and approve access.
  4. If the provider exposes pages/profiles/channels, choose where you want to post.

Your credentials are stored against your tenant, with the access token encrypted at rest. Providers marked Unavailable have not been enabled by your administrator.

Write and publish a post

  1. Open Social → New post.
  2. Write your content and optionally add an image URL.
  3. Select the providers you want to publish to.
  4. Choose Post now or Schedule for later and pick a date and time.

Posts that target a provider without an active connection are marked failed for that provider while still publishing to the others. The post list shows a status per provider, so you can see exactly what succeeded.

How scheduling works

Scheduled posts are stored with a ScheduledFor timestamp. A background worker runs every 30 seconds, checks each tenant's database for due posts, and publishes them. This is the same per-tenant worker pattern used by workflows and bulk actions.

The worker runs only when all of these are true:

  • BackgroundEngine is enabled.
  • ProcessSocialPosts is enabled.
  • SocialPosting is enabled.
  • The tenant has an active Social Posting licence.

Retries: failed posts are retried up to three times, 10 minutes apart. After that they remain Failed (or Partially failed) and show the error on the post card.

For administrators: enabling providers

Add the platform's OAuth app credentials once in appsettings.json. These are shared by all tenants; users never see the client secret.

One callback for every tenant. Set CallbackUrl to a single public URL on your platform domain and register only that URL with each provider. The tenant travels in a signed state; after the provider redirects back, the CRM hands the result to the tenant's own host (subdomain or custom domain) via a short-lived, single-use token to finish the connection and show the page picker. If CallbackUrl is empty the current host is used (single-host).

"SocialOAuth": {
  "CallbackUrl": "https://app.surelycrm.co.uk/Social/Callback",
  "Providers": {
    "Instagram": {
      "ClientId": "your-client-id",
      "ClientSecret": "your-client-secret",
      "AuthorizeUrl": "https://www.facebook.com/v19.0/dialog/oauth",
      "TokenUrl": "https://graph.facebook.com/v19.0/oauth/access_token",
      "Scopes": "instagram_basic,pages_show_list,instagram_content_publish",
      "AccountsUrl": "https://graph.facebook.com/v19.0/me/accounts"
    },
    "X": {
      "ClientId": "your-client-id",
      "ClientSecret": "your-client-secret",
      "AuthorizeUrl": "https://twitter.com/i/oauth2/authorize",
      "TokenUrl": "https://api.twitter.com/2/oauth2/token",
      "Scopes": "tweet.read tweet.write users.read offline.access"
    }
  }
}

Register the callback URL https://your-crm/Social/Callback with each provider. Providers without credentials appear as Unavailable to users.

Pages and profiles: when AccountsUrl is set, the user is shown a picker after signing in (Facebook Pages, LinkedIn organisations, YouTube channels, etc.). Single-account providers such as X connect directly with no picker.

Where data is stored

All social data lives in the tenant's own database:

TablePurpose
SocialProviderConnectionsEncrypted credentials per provider account.
SocialPostsPost content, status and schedule.
SocialPostTargetsOne row per provider, with its publish result.

Provider APIs: each platform requires its own OAuth app and API credentials. The publishing layer is pluggable — add a provider adapter to call the platform's API with the stored token.