Umbraco.Community.Automate.DevTo 1.0.0-beta.3

This is a prerelease version of Umbraco.Community.Automate.DevTo.
dotnet add package Umbraco.Community.Automate.DevTo --version 1.0.0-beta.3
                    
NuGet\Install-Package Umbraco.Community.Automate.DevTo -Version 1.0.0-beta.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Umbraco.Community.Automate.DevTo" Version="1.0.0-beta.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Umbraco.Community.Automate.DevTo" Version="1.0.0-beta.3" />
                    
Directory.Packages.props
<PackageReference Include="Umbraco.Community.Automate.DevTo" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Umbraco.Community.Automate.DevTo --version 1.0.0-beta.3
                    
#r "nuget: Umbraco.Community.Automate.DevTo, 1.0.0-beta.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Umbraco.Community.Automate.DevTo@1.0.0-beta.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Umbraco.Community.Automate.DevTo&version=1.0.0-beta.3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Umbraco.Community.Automate.DevTo&version=1.0.0-beta.3&prerelease
                    
Install as a Cake Tool

Umbraco.Community.Automate.DevTo

A DEV Community (dev.to) connection and action for Umbraco Automate.

Cross-post your Umbraco content to DEV automatically when you publish it. Markdown, Rich Text, Block List and Block Grid content is converted to Markdown, relative links and images are made absolute, and the canonical URL points back at your site so search engines treat it as the original.

Works with Umbraco 17 and 18 (and Umbraco Automate 17 and 18).

Installation

dotnet add package Umbraco.Community.Automate.DevTo

No further setup required. The composer registers itself automatically via Umbraco's IComposer discovery.

Setup

1. Generate a DEV API key

On DEV, go to Settings → Extensions → DEV Community API Keys, give the key a description and click Generate API Key.

2. Store the key as a secret

Rather than pasting values into the backoffice, store them in the package's Umbraco:Community:Automate:DevTo section, split into Variables (non-sensitive values) and Secrets (sensitive values). The package registers both with Umbraco Automate's configuration allow-list, so no extra setup is needed; Secrets can only be referenced from sensitive fields such as API Key.

{
  "Umbraco": {
    "Community": {
      "Automate": {
        "DevTo": {
          "Variables": {
            "SiteUrl": "https://your-site.com"
          },
          "Secrets": {
            "ApiKey": "your-api-key"
          }
        }
      }
    }
  }
}

For production, use environment variables instead:

Umbraco__Community__Automate__DevTo__Secrets__ApiKey=your-api-key
Umbraco__Community__Automate__DevTo__Variables__SiteUrl=https://your-site.com

Reference these values from the backoffice with a $ prefix, e.g. $Umbraco:Community:Automate:DevTo:Variables:SiteUrl.

SiteUrl is only needed if Umbraco can't generate absolute URLs for your content (see URLs).

3. Create the connection

  1. Go to Automate → Connections and create a new DEV Community connection.
  2. API Key: $Umbraco:Community:Automate:DevTo:Secrets:ApiKey
  3. Click Test connection. You should see "Connected as @yourname".

Posting to a different Forem community? Change Instance URL under Advanced.

Reference not resolving? The reference must start with $Umbraco:Community:Automate:DevTo:Variables: or $Umbraco:Community:Automate:DevTo:Secrets:, and the key must exist in configuration. If you previously used Automate's shared Umbraco:Automate:Variables / Umbraco:Automate:Secrets sections, move those values here; the Markdown preview only resolves Variables from the new section.

Cross-posting blog posts

Create an automation:

  1. Trigger: Content Published, with Content Types set to your blog post type.
  2. Action: Get Content, with Content Key ${ trigger.contentKey }. Only needed if you bind tags, a description or a cover image from the post's properties (below).
  3. Action: Publish Content to DEV, with your DEV connection.
Setting Description
Body Properties The properties holding the body, in order. Click Choose from a document type…, pick your blog post type and tick its body properties (only Markdown, Rich Text, Block List, Block Grid and Textarea properties are listed, including ones from compositions), or type an alias.
Title Defaults to ${ trigger.contentName }. Bind a property instead, e.g. ${ steps.getContent.properties.pageTitle }, or compose one: ${ trigger.contentName } \| My Blog. Blank uses the content name.
Tags Typed (umbraco, dotnet), bound from a Tags or picker property (${ steps.getContent.properties.tags }), or both: umbraco, ${ steps.getContent.properties.categories }.
Publish immediately Off (default): new articles are saved as drafts on DEV. On: they're published.
Description A summary for feeds and link previews, e.g. ${ steps.getContent.properties.metaDescription }. HTML is stripped.
Cover Image A URL, or a bound media picker, e.g. ${ steps.getContent.properties.mainImage }.
Series Links articles together as a series on DEV.
Content Key Advanced. The item to post. Defaults to ${ trigger.contentKey }.
Culture Advanced. For variant content. Blank uses the default culture.
Site URL Advanced. Your public base URL, e.g. $Umbraco:Community:Automate:DevTo:Variables:SiteUrl.
Canonical URL Advanced. Override the canonical URL.
Existing Article ID Advanced. Update this DEV article instead of looking one up.

Title, Tags, Description and Cover Image take ${ } bindings, so they can come from the trigger, a Get Content step or any earlier step, with filters such as | truncate:100. Bound pickers and media arrive as JSON; the action reads the names (for tags) and URLs (for images) out of it.

Body Properties are properties rather than bindings on purpose: converting blocks, media and Markdown needs the content itself, which a binding (a JSON copy of the values) can't provide. Picked properties are stored with the document type they came from, so reopening the step shows their names and flags any alias the type no longer has (after a property is renamed, for example). Typed aliases are stored as a plain list (e.g. intro, contentRows).

Tags are lowercased and stripped to letters and numbers, as DEV requires ("Umbraco CMS" becomes umbracocms), and only the first four are used.

Updates, not duplicates

Before posting, the action looks through your DEV articles (published and drafts) for one with the same canonical URL. If it finds one, it updates it; otherwise it creates a new one. So re-publishing a post in Umbraco updates its copy on DEV, and a retried step never posts twice. No extra property on your document type is needed.

If you change a post's URL, the lookup won't find the old article. Set Existing Article ID to point it at the right one.

Leaving Publish immediately off never unpublishes. New articles are created as drafts, and an article you've already published on DEV stays published when it's updated.

Outcomes and outputs

The action produces a created, updated or notFound outcome (notFound means the content was unpublished before the step ran), so later steps can branch. For example, announce new posts on Mastodon only on created, so edits don't post again.

Its output is available to later steps:

Output Example
${ steps.<alias>.url } https://dev.to/you/my-post-1a2b
${ steps.<alias>.articleId } 1234567
${ steps.<alias>.slug } my-post-1a2b
${ steps.<alias>.published } false
${ steps.<alias>.canonicalUrl } https://your-site.com/blog/my-post/

Previewing

Click Preview… under Body Properties and pick a published item to see the Markdown and canonical URL the step would post, or the error it would fail with. DEV isn't called. Bindings only have values during a run, so the preview uses what a blank setting would for any that use one (Site URL, Culture, Canonical URL); $Umbraco:Community:Automate:DevTo:Variables:… references are resolved, secrets never are.

Finding the article on DEV

Once a post has gone out, its document's Info tab shows a DEV box with a link to the article, whether it was a draft or published when it was last posted, and when. Variant content gets a link per culture. The link is kept in Umbraco's key-value store, so your document type doesn't need a property for it. Automate's run history doesn't show step output, so this is the place to look.

The backoffice never calls DEV by itself. Click Check on DEV to ask DEV for the article's current state, using the connection it was posted with: it picks up an article you've since published (and its new URL), or marks one you've deleted as Deleted on DEV, with a button to remove the link. Publishing the page again posts a new article either way. Links saved by earlier versions of the package didn't record their connection; they're checked with your DEV connection if you only have one.

Reviewing before publishing

Leave Publish immediately off and publish on DEV yourself, or add Automate's Request Approval step followed by a second Publish Content to DEV step with Publish immediately on.

How content is converted

Content Becomes
Markdown editor The Markdown you wrote, unchanged.
Rich Text Converted from HTML. Blocks in the editor are rendered by your site's partial views first. Video embeds (YouTube, Vimeo, …) become DEV {% embed %} tags.
Block List / Block Grid Each block's properties in order, including nested blocks and grid areas.
Textstring / Textarea (in a block) Text. A Textstring whose alias ends in heading, headline or title becomes a ## heading.
Media picker (in a block) An image, using the media's altText property or its name as alt text.
Multi URL picker (in a block) Links.
Code blocks A block with a code (or codeSnippet, snippet, sourceCode, codeBlock) property becomes a fenced code block, highlighted using a language, lang, codeLanguage or syntax property.
Anything else Left out: toggles, colours, content pickers and so on have no place in an article.

Relative links and image URLs are made absolute. Code samples are left untouched.

Custom blocks

If the built-in conversion doesn't suit a block, implement IDevToBlockConverter and register it:

public class CalloutConverter : IDevToBlockConverter
{
    public string? Convert(IPublishedElement content, IPublishedElement? settings, DevToConversionContext context)
        => content.ContentType.Alias switch
        {
            "callout" => $"> **Note:** {context.ConvertProperty(content, "text")}",
            "newsletterSignup" => "",   // leave this block out
            _ => null,                  // not mine: use the next converter / built-in conversion
        };
}

public class DevToConvertersComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
        => builder.Services.AddSingleton<IDevToBlockConverter, CalloutConverter>();
}

DevToConversionContext gives you the same helpers the built-in conversion uses: ConvertProperty, ConvertElement (for nested blocks), HtmlToMarkdown, ResolveUrl and GetMediaUrl.

URLs

DEV needs absolute URLs for the canonical link, links and images. By default the action uses the absolute URL Umbraco generates for the content, which works when the site has a domain assigned (Culture and Hostnames) or Umbraco:CMS:WebRouting:UmbracoApplicationUrl is set. Otherwise, set Site URL on the step, e.g. to $Umbraco:Community:Automate:DevTo:Variables:SiteUrl.

Errors and retries

API failures are classified so Automate can decide what to do: rate limiting (429), timeouts and DEV being unavailable (5xx) are transient and retried according to the step's error behaviour; an invalid API key, a rejected article (422) or missing settings fail straight away with the DEV error message.

Compatibility

One build of the package supports Umbraco 17 and 18. It's compiled against 17 and every change is tested on both, including running the 17 build on 18 and checking every Umbraco API it calls still exists there. Umbraco 19 isn't supported until it has been tested.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-beta.3 51 9/30/2026
1.0.0-beta.2 59 9/30/2026
1.0.0-beta.1 46 9/30/2026