Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Set up headless Optimizely Forms API

Describes the most important steps to get started with Optimizely Headless Form API.

Install packages

Optimizely Headless Form API consists of one main NuGet package, Optimizely.Cms.Forms.Service, and some additional NuGet packages let you install only the necessary functionality.

Install the NuGet packages in your solution using the NuGet Package Manager in Visual Studio or with the command line:

dotnet add package Optimizely.Cms.Forms.Service

For authentication, install Episerver.OpenIDConnect package using the NuGet Package Manager in Visual Studio or with the command line:

dotnet add package Episerver.OpenIDConnect

Configure form headless API options

Configure the API in ConfigureServices in startup.cs.

services.AddOptimizelyFormsService(options => {
  options.EnableOpenApiDocumentation = true;
  options.FormCorsPolicy = new FormCorsPolicy {
    AllowOrigins = new string[] {
        "*"
      }, //Enter '*' to allow any origins, multiple origins separate by comma  
      AllowCredentials = true
  };
  options.OpenIDConnectClients.Add(new() {
    Authority = "" //Enter the client's domain that requires authentication. The domain must include the protocol, for example, https://localhost:3000.
  });
});

Configure encryption key for OpenIDConnect.

public class FormServiceOptionsPostConfigure : IPostConfigureOptions<OptimizelyFormsServiceOptions>
{
    private readonly OpenIddictServerOptions _options;

    public FormServiceOptionsPostConfigure(IOptions<OpenIddictServerOptions> options)
    {
        _options = options.Value;
    }

    public void PostConfigure(string name, OptimizelyFormsServiceOptions options)
    {
        foreach (var client in options.OpenIDConnectClients)
        {
            foreach (var key in _options.EncryptionCredentials.Select(c => c.Key))
            {
                client.EncryptionKeys.Add(key);
            }

            foreach (var key in _options.SigningCredentials.Select(c => c.Key))
            {
                client.SigningKeys.Add(key);
            }
        }
    }
}

Configure OpenIdConnect in Startup.cs.

services.AddOpenIDConnect<ApplicationUser> (
  useDevelopmentCertificate: true,
  signingCertificate: null,
  encryptionCertificate: null,
  createSchema: true,
  options => {
    options.AllowResourceOwnerPasswordFlow = true;
    options.AccessTokenLifetime = TimeSpan.FromHours(8);
    options.RequireHttps = false;
    options.Applications.Add(new OpenIDConnectApplication {
      ClientId = "ClientId",
        Scopes = {
          Scopes.OpenId,
        },
    });
  });

services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<OptimizelyFormsServiceOptions>, FormServiceOptionsPostConfigure>());

See OpenID Connect Authentication quick guide for details.

Test and verify the API.

PUT or GET endpoint description

You should have the following endpoints available on your host site:

  • GET – /_forms/v1/forms/{ContentGuid}?language={Language} – Retrieve form data with form elements by content guid
  • PUT – /_forms/v1/forms/ – Submit the form.

To verify that the API is working properly, open the Form Headless API in your browser:

http://<your-site-url>/_forms/v1/forms/<ContentGuid>?language=en

You should now get a 200 OK response and see the site and its associated properties:

{
  "key": "41A07E4D3A634CC581F399CAAC75789C",
  "properties": {
    "title": "",
    "allowToStoreSubmissionData": true,
    "description": "",
    "showSummarizedData": false,
    "confirmationMessage": "",
    "resetConfirmationMessage": "",
    "redirectToPage": "",
    "submitSuccessMessage": "",
    "allowAnonymousSubmission": false,
    "allowMultipleSubmission": false,
    "showNavigationBar": false
  },
  "formElements": [{
      "key": "2933aa8d3e794ccf89ff63bd9746d9f8",
      "contentType": "SelectionElementBlock",
      "displayName": "Selection",
      "properties": {
        "label": "Selection",
        "description": "",
        "validators": [{
          "type": "RequiredValidator",
          "description": null,
          "model": {
            "message": "This field is required.",
            "validationCssClass": "ValidationRequired",
            "additionalAttributes": {
              "required": "",
              "aria-required": "true"
            }
          }
        }],
        "allowMultiSelect": false,
        "feed": "FormsFeed_UseManualInput",
        "items": [{
            "caption": "Value1",
            "value": "Value1",
            "checked": true
          },
          {
            "caption": "Value2",
            "value": "Value2",
            "checked": false
          }
        ],
        "placeHolder": "",
        "autoComplete": "",
        "satisfiedAction": "",
        "conditionCombination": "All",
        "conditions": ""
      },
      "localizations": {
        "selectionDefaultPlaceholder": "-- Select an option --"
      }
    },
    {
      "key": "362b97255ed0483aa096f062e189d857",
      "contentType": "NumberElementBlock",
      "displayName": "Number",
      "properties": {
        "label": "Number",
        "description": "",
        "validators": [{
          "type": "NumericValidator",
          "description": null,
          "model": {
            "message": "Enter a valid number.",
            "validationCssClass": null,
            "additionalAttributes": null
          }
        }],
        "placeHolder": "",
        "autoComplete": "",
        "satisfiedAction": "",
        "conditionCombination": "All",
        "conditions": ""
      },
      "localizations": {}
    },
    {
      "key": "05524d25f3084ba6b2437e4529ddcdcb",
      "contentType": "FormStepBlock",
      "displayName": "Form step",
      "properties": {
        "label": "",
        "description": "",
        "attachedContentLink": "",
        "dependCondition": "",
        "dependValue": ""
      },
      "localizations": {
        "previousButtonLabel": "Previous step",
        "nextButtonLabel": "Next step",
        "pageButtonLabel": "Step"
      }
    },
    {
      "key": "426f2661d0da4751955bfc67503c556d",
      "contentType": "TextareaElementBlock",
      "displayName": "Text area",
      "properties": {
        "label": "Text area",
        "description": "",
        "validators": [],
        "placeHolder": "",
        "autoComplete": "",
        "satisfiedAction": "",
        "conditionCombination": "All",
        "conditions": ""
      },
      "localizations": {}
    },
    {
      "key": "97dd12c78d88450695dd231216c8dc1d",
      "contentType": "SubmitButtonElementBlock",
      "displayName": "Submit button",
      "properties": {
        "finalizeForm": false,
        "label": "Submit",
        "image": "",
        "description": "",
        "redirectToPage": "",
        "satisfiedAction": "",
        "conditionCombination": "All",
        "conditions": ""
      },
      "localizations": {
        "label": "Submit"
      }
    }
  ],
  "locale": "en",
  "localizations": {
    "allowAnonymousSubmissionErrorMessage": "You must be logged in to submit this form. If you are logged in and still cannot post, make sure \"Do not track\" in your browser settings is disabled.",
    "allowMultipleSubmissionErrorMessage": "You already submitted this form."
  }
}

PUT /_forms/v1/forms

Submit single or multiple step form. The API accepts both application/json and multipart/form-data content types.

URL

/_forms/v1/forms

Method

PUT

Headers

  • Content-Type – application/json or multipart/form-data
  • Authorization – Bearer {token}

Request Body for application/json

When using the application/json content type, the request body should be a JSON object with the following fields:

{
  "formKey":"",
  "locale":"",
  "isFinalized": true,
  "submissionKey":"",
  "hostedPageUrl":"",
  "currentStep":0,
  "fields":{
    "keyOfField1":"value1",
    "keyOfField1":"value2",
    ...
  }
}

Example:

{
  "formKey":"6001a245d28a4419baf28f36fc3cf2a9",
  "locale":"en",
  "isFinalized": true,
  "submissionKey":"",
  "hostedPageUrl":"",
  "currentStep":0,
  "fields":{
    "fd8c0ce15f56440abee3358a9fe9c9d8":"test1",
    "b176ccda9f9f4ea4b87d57cf1eac8210":"test2",
  }
}

Parameters for request

  • formKey – The key of the form's submitted data.
  • locale – The locale of the submitted form.
  • isFinalized – Indicate whether the form submission is finalized.
  • submissionKey – The key of the form's submitted data. This submissionKey is used in multi-step forms and is returned from the previous step's submitted response.
  • hostedPageUrl – The URL of the page hosted the form step.
  • currentStep – The index of the current form step in submission.
  • fields – Key value collection of submitted data.

Request Body for multipart/form-data

When using the multipart/form-data content type, the request body should include:

  • A list of files, each identified by a unique GUID key.
  • A data field containing a JSON string that conforms to the structure defined in the application/json part.

Example request

PUT /_forms/v1/forms HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Authorization: Bearer YOUR_TOKEN

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="data"
{
  "formKey":"6001a245d28a4419baf28f36fc3cf2a9",
  "locale":"en",
  "isFinalized":true,
  "submissionKey":"",
  "hostedPageUrl":"",
  "currentStep":0,
  "fields": {
    "fd8c0ce15f56440abee3358a9fe9c9d8":"test1",
    "b176ccda9f9f4ea4b87d57cf1eac8210":"test2"
  }
}

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="123e4567-e89b-12d3-a456-426614174000"; filename="file1.jpg"
Content-Type: image/jpeg

(binary data)
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="123e4567-e89b-12d3-a456-426614174001"; filename="file2.jpg"
Content-Type: image/jpeg

(binary data)
------WebKitFormBoundary7MA4YWxkTrZu0gW--

Authentication

Configure the signing or encryption key and authority to verify the authentication JSON WebToken (jwt) in the back end:

services.AddOptimizelyFormsService(options => {
  options.OpenIDConnectClients.Add(new() {
    Authority = http://host:port,
    EncryptionKeys = ,
    SigningKeys = 
  });
});

Configure Swagger

Install the NuGet packages in your solution using the NuGet Package Manager in Visual Studio or with the command line:

dotnet add package Swashbuckle.AspNetCore

Configure Swagger in startup.cs.

services.AddSwaggerGen(c => {
  c.SwaggerDoc("v1", new OpenApiInfo {
    Title = "AlloySampleSite Custom API",
      Version = "v1",
  });
});

app.UseSwagger();

app.UseSwaggerUI(options => {
  options.SwaggerEndpoint("/_forms/v1/docs/openapi.json", "Optimizely Headless Form API V1");
  options.SwaggerEndpoint("/swagger/v1/swagger.json", "AlloySampleSite Custom API V1");
  options.OAuthClientId("ClientId");
  options.OAuthClientSecret("ClientSecret");
});

Use Postman

Postman (postman.com) is a free tool that you can use as an API client to send REST calls. With Postman, you can connect to APIs and simulate calls to endpoints and their responses without setting up a backend server.
To test the Form Headless API, you can use a tool such as Postman and send a request to one of the endpoints mentioned in the above section.