OmniSocialsOmniSocials
DocumentationAPI reference
Help centerSupport
  • Documentation
  • API reference
Developers
  • Quickstart
  • API reference
  • SDKs
  • MCP server
  • CLI
For AI agents
  • Docs for AI agents
  • llms.txt
  • OpenAPI spec
  • Postman collection
Product
  • OmniSocials
  • Pricing
  • Changelog
  • Help center
Company
  • Support
  • Privacy policy
  • Terms of service

© 2026 OmniSocials

OmniSocials on GitHubOmniSocials on XOmniSocials on LinkedIn
Overview
Get started
SDKs
    Node.jsPythonGoRubyJavaPHP.NETRust
CLI
MCPDocs for AI agentsWebhooks
Guides
Platforms
Integrations
Migrate
Plans and billing
GlossaryChangelogAPI reference
SDKs

.NET SDK reference: install, auth and methods

The official .NET SDK. Targets .NET 8, depends only on System.Net.Http and System.Text.Json, fully async with CancellationToken support on every method.

This page is the reference. For an overview of the .NET SDK, see omnisocials.com/sdk/dotnet.

Install

TerminalTerminal
dotnet add package OmniSocials

Authentication

C#
var client = new OmniSocialsClient(new OmniSocialsOptions { ApiKey = "omsk_live_..." });

Reads OMNISOCIALS_API_KEY if omitted. A missing key throws AuthenticationException. The client owns an internal HttpClient and implements IDisposable: create one and reuse it.

Create a post

C#
var response = await client.Posts.CreateAsync(new PostCreateParams { Content = "New drop this Friday", Channels = new[] { "instagram", "facebook", "linkedin" }, ScheduledAt = "2026-08-01T09:00:00Z", MediaUrls = new[] { "https://example.com/teaser.jpg" }, }); var post = response!.Value.GetProperty("data"); Console.WriteLine($"{post.GetProperty("id")} {post.GetProperty("status")}");

MediaUrls / MediaIds entries can also be objects carrying per-media alt text (new { url, alt } / new { id, alt }, max 1500 chars), delivered to Mastodon, Bluesky, X (photos), Pinterest, Instagram (images), and LinkedIn (images):

C#
MediaUrls = new object[] { new { url = "https://example.com/teaser.jpg", alt = "Red sneaker on a white background" } },

Publish immediately:

C#
await client.Posts.CreateAndPublishAsync(new PostCreateParams { Content = "Going live right now", Channels = new[] { "x", "bluesky" }, });

Upload media

C#
var upload = await client.Media.UploadFromUrlAsync(new MediaUploadFromUrlParams { Url = "https://example.com/launch-video.mp4", Name = "launch-video-v2", Folder = "Campaigns", }); Console.WriteLine(upload!.Value.GetProperty("data").GetProperty("id"));

Local files via factory methods:

C#
// From a path await client.Media.UploadAsync(MediaUploadParams.FromFile("./photos/product.jpg", name: "product-hero")); // Or from bytes / a stream var bytes = await File.ReadAllBytesAsync("./photos/product.jpg"); await client.Media.UploadAsync(MediaUploadParams.FromBytes(bytes, "product.jpg")); await using var stream = File.OpenRead("./photos/product.jpg"); await client.Media.UploadAsync(MediaUploadParams.FromStream(stream, "product.jpg"));

Analytics

C#
// One post's latest per-platform metrics var stats = await client.Analytics.PostAsync("post_id"); Console.WriteLine(stats!.Value.GetProperty("data").GetProperty("platforms")); // Batch: up to 100 posts in one call var batch = await client.Analytics.PostsAsync(new[] { "id1", "id2", "id3" }); // Workspace-wide overview var overview = await client.Analytics.OverviewAsync(new AnalyticsOverviewParams { Period = "30d" }); var totals = overview!.Value.GetProperty("data"); Console.WriteLine($"{totals.GetProperty("total_impressions")} impressions"); // Account-level stats (followers etc) var accountStats = await client.Analytics.AccountsAsync(new AccountAnalyticsParams { Platform = "instagram" });

Social Inbox

Read and answer DMs, comments and mentions from your connected accounts. The API key needs the opt-in inbox:read / inbox:write scopes. The lists use cursor pagination (Cursor in, pagination.next_cursor out). Optional typed models are available (InboxNextResponse, InboxReplyResponse, InboxDeleteMessageResponse).

C#
var page = await client.Inbox.ListConversationsAsync(new InboxConversationListParams { Platform = "instagram", Unread = true, }); var conversationId = page!.Value.GetProperty("data")[0].GetProperty("conversation_id").GetString()!; var messages = await client.Inbox.GetMessagesAsync(conversationId); await client.Inbox.ReplyAsync(conversationId, new InboxReplyParams { Text = "Thanks for reaching out!" }); await client.Inbox.MarkReadAsync(conversationId); // Comment moderation: hide (hide: false unhides) or delete var messageId = messages!.Value.GetProperty("data")[0].GetProperty("id").GetString()!; await client.Inbox.HideAsync(messageId); await client.Inbox.DeleteMessageAsync(messageId);

NextAsync hands out the next conversation that still needs an answer, with the thread and the post it belongs to. On a comment thread, set MessageId so the reply goes under the comment you answer and not under the newest comment on the post.

C#
var next = await client.Inbox.NextAsync(); var item = next!.Value.GetProperty("data"); while (item.ValueKind != JsonValueKind.Null) { var message = item.GetProperty("message"); if (!item.GetProperty("reply_window").GetProperty("open").GetBoolean()) { // An Instagram/Facebook DM past Meta's 24-hour window: answer it in the app await client.Inbox.MarkReadAsync(message.GetProperty("conversation_id").GetString()!); next = await client.Inbox.NextAsync(); item = next!.Value.GetProperty("data"); continue; } var reply = await client.Inbox.ReplyAsync(message.GetProperty("conversation_id").GetString()!, new InboxReplyParams { Text = "Thanks! DM sent.", MessageId = message.GetProperty("id").GetString(), IncludeNext = true, }); item = reply!.Value.GetProperty("next"); }

See the Social Inbox guide for the platforms, the reply rules and the error codes.

Pinterest product tags

Tag products on a Pin so people can shop the items in the image. List the product Pins of the connected Pinterest account, then pass up to 24 of their Pin ids as ["product_tags"] in the Pinterest options of the post. Only product Pins of your own account can be tagged; products of other merchants cannot.

C#
var result = (await client.Pinterest.ListProductsAsync())!.Value; if (result.TryGetProperty("error", out var error)) { Console.Error.WriteLine(error.GetProperty("code").GetString()); } else { var productTags = result.GetProperty("products").EnumerateArray() .Take(3) .Select(product => product.GetProperty("pin_id").GetString()!) .ToArray(); await client.Posts.CreateAsync(new PostCreateParams { Content = "Our summer picks", Channels = new[] { "pinterest" }, MediaUrls = new[] { "https://example.com/summer-look.jpg" }, ScheduledAt = "2026-08-01T09:00:00Z", Pinterest = new Dictionary<string, object?> { ["board_id"] = "1234567890", ["product_tags"] = productTags, }, }); }

The list answers with HTTP 200 in both cases: products on success, or an error object (pinterest_not_connected, pinterest_catalog_access_required, platform_error) when it could not be read. It reads the Pinterest catalog when the connection has catalog access (given one time in the OmniSocials composer: Pinterest options, Add products, Connect catalog), else the account's own Pins. The tags are added right after the Pin is published. A product that Pinterest refuses never fails the post: the post then carries pinterest.product_tags_result with the tagged and the skipped Pin ids. client.Pinterest.ValidateProductAsync(id) checks one Pin id or Pin link before you post. On Posts.UpdateAsync the Pinterest dictionary replaces the stored one, so leave ["product_tags"] out to remove the tags.

Verify webhooks

ASP.NET Core minimal API:

C#
using System.Text.Json; using OmniSocials; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapPost("/omnisocials/webhook", async (HttpRequest request) => { using var reader = new StreamReader(request.Body); var payload = await reader.ReadToEndAsync(); // raw body, do not re-serialize var signature = request.Headers["X-OmniSocials-Signature"].ToString(); var secret = Environment.GetEnvironmentVariable("OMNISOCIALS_WEBHOOK_SECRET")!; JsonElement webhookEvent; try { webhookEvent = WebhookSignature.Verify(payload, signature, secret, toleranceSeconds: 300); } catch (WebhookVerificationException) { return Results.BadRequest(); } switch (webhookEvent.GetProperty("type").GetString()) { case "post.published": var data = webhookEvent.GetProperty("data"); Console.WriteLine($"Published: {data.GetProperty("post_id")} {data.GetProperty("targets")}"); break; case "post.failed": Console.Error.WriteLine($"Failed: {webhookEvent.GetProperty("data").GetProperty("post_id")}"); break; case "post.rejected": var rejected = webhookEvent.GetProperty("data"); Console.Error.WriteLine($"Rejected: {rejected.GetProperty("post_id")} {rejected.GetProperty("approval").GetProperty("reason")}"); break; } return Results.Ok(); }); app.Run();

Events: post.scheduled, post.published, post.failed, post.approved and post.rejected. The two approval events carry data.approval with status, decided_by (the approver's user id) and reason (null on post.approved). To read the full review of a post (steps, approvers, the rejection reason and the comment thread), call client.Posts.GetApprovalAsync(id).

Errors

All exceptions extend OmniSocialsException. ApiException exposes Status, Code, Message, and Body. Types: ValidationException (400/422), AuthenticationException (401), PermissionDeniedException (403), NotFoundException (404), RateLimitException (429, RetryAfter), ServerException (5xx), ApiConnectionException, WebhookVerificationException.

C#
try { await client.Posts.CreateAsync(new PostCreateParams { Content = "Hi", Channels = new[] { "instagram" }, }); } catch (RateLimitException ex) { Console.WriteLine($"Rate limited, retry in {ex.RetryAfter}s"); } catch (ValidationException ex) { Console.WriteLine($"Bad request ({ex.Code}): {ex.Message}"); } catch (ApiConnectionException ex) { Console.WriteLine($"Network problem: {ex.Message}"); } catch (ApiException ex) { Console.WriteLine($"API error {ex.Status} ({ex.Code}): {ex.Message}"); }

Configuration

C#
var client = new OmniSocialsClient(new OmniSocialsOptions { ApiKey = "omsk_live_...", BaseUrl = "https://api.omnisocials.com/v1", // default Timeout = TimeSpan.FromSeconds(30), // per-request timeout (default 30s) MaxRetries = 2, // automatic retries on 429 / 5xx / network errors (default 2) });

Timeout is a TimeSpan and applies per attempt.

Notes

  • Every method ends in ...Async and returns Task<JsonElement?> (raw JSON)
  • Update helpers: MoveToRoot = true (media) / MoveToTopLevel = true (folders) distinguish "leave unchanged" from "set to null"
  • Dictionary entries with explicit nulls are sent on the wire; null POCO properties are omitted
  • Source: github.com/OmniSocials/omnisocials-dotnet
Last modified on October 21, 2026
PHPRust
On this page
  • Install
  • Authentication
  • Create a post
  • Upload media
  • Analytics
  • Social Inbox
  • Pinterest product tags
  • Verify webhooks
  • Errors
  • Configuration
  • Notes
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#