Create, update, and validate custom Creatio Configuration Web Services and their tests. Use when users need to expose custom backend endpoints in Creatio, wire service contracts/implementations, return structured success/error results, or verify endpoints with integration-style and unit tests.
Create deterministic steps for implementing a Creatio custom web service in a package and testing it before delivery. Follow this skill when requests involve "create endpoint", "add web service method", "test service", "mock user/session", or "verify HTTP contract" in Creatio.
Place service classes in:
packages/<PACKAGE_NAME>/src/cs/EntryPoints/WebServiceIf the repository stores C# files under Files/src, map the same logical location as:
packages/<PACKAGE_NAME>/Files/src/cs/EntryPoints/WebServiceUse namespace pattern:
<PackageNamespace>.EntryPoints.WebServicePlace tests in:
tests/<PACKAGE_NAME>/EntryPoints/WebServiceErrorOr.Use references/creatio-webservice-template.md for a concrete structure and checklist.
When method signature is:
public <ResponseDto> <MethodName>(<RequestDto> request)Creatio uses default serializer/deserializer for request/response binding (JSON/XML to DTO and DTO to response) and commonly returns 200 OK for the operation result flow.
If custom HTTP status behavior is required:
HttpContextAccessor.GetInstance().Response.StatusCode (NETSTANDARD2_0) or legacy response context.void method and write payload directly to Response.OutputStream when you need full control over status + body format.Self-contained example (DemoService-style pattern, no repository dependency):
[OperationContract]
[WebInvoke(Method = "GET", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Json)]
public void GetExample() {
HttpRequest request = HttpContextAccessor.GetInstance().Request;
#if NETSTANDARD2_0
HttpResponse response = HttpContextAccessor.GetInstance().Response;
response.StatusCode = 200;
#endif
var payload = new { text = "Hello World" };
string json = System.Text.Json.JsonSerializer.Serialize(payload);
byte[] bytes = Encoding.UTF8.GetBytes(json);
response.OutputStream.Write(bytes, 0, bytes.Length);
response.OutputStream.Flush();
}
Use this structure for configuration web services:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class <ServiceName> : BaseService, IReadOnlySessionState {
[OperationContract]
[WebInvoke(
Method = "POST",
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Json)]
public <ResponseDto> <MethodName>(<RequestDto> request) {
HttpRequest requestInstance = HttpContextAccessor.GetInstance().Request;
#if NETSTANDARD2_0
HttpResponse response = HttpContextAccessor.GetInstance().Response;
#endif
// Validate and execute via app layer; do not throw expected errors.
// Return ErrorOr-driven result mapped to status code and response DTO.
}
}
Endpoint format:
https://<creatio-host>/rest/<ServiceName>/<MethodName>Create or update tests for every production change.
ErrorOr) without throwing.POST/GET https://<creatio-host>/rest/<ServiceName>/<MethodName> with authenticated Creatio session.Test fixture location and naming:
tests/<PACKAGE_NAME>/EntryPoints/WebService<ServiceName>TestFixture.cs<PackageNamespace>.Tests.EntryPoints.WebServiceCreate test class in this order:
BaseMarketplaceTestFixture.[MockSettings(RequireMock.All)].[TestFixture(Category = "PreCommit")].IHttpContextAccessor with mocked HttpContext in SetUp.Self-contained fixture template (DemoServiceTestFixture-style, no repository dependency):
using FluentAssertions;
using NSubstitute;
using NUnit.Framework;
using Terrasoft.Configuration.Tests;
using Terrasoft.Web.Http.Abstractions;
namespace <PackageNamespace>.Tests.EntryPoints.WebService {
[MockSettings(RequireMock.All)]
[TestFixture(Category = "PreCommit")]
public class <ServiceName>TestFixture : BaseMarketplaceTestFixture {
private readonly HttpContext _context = Substitute.For<HttpContext>();
private IHttpContextAccessor _httpContextAccessor;
private System.IO.MemoryStream _outputStream;
protected override void SetUp() {
base.SetUp();
_outputStream = new System.IO.MemoryStream();
_context.Response.OutputStream.Returns(_outputStream);
_httpContextAccessor = CustomSetupHttpContextAccessor(_context, UserConnection);
}
protected override void TearDown() {
base.TearDown();
_outputStream.Dispose();
}
}
}
Test method requirements:
[Test] and [Description("...")] for every test.HttpContextAccessor.Required assertions per endpoint method:
Response.StatusCode.For JSON body assertions:
response.OutputStream.Seek(0, Begin) before read.Self-contained test example (DemoService-style assertion):
[Test]
[Description("Returns OK and serialized payload for custom response writer")]
public void GetExample_ReturnsExpectedJson() {
ExampleService sut = new ExampleService {
HttpContextAccessor = _httpContextAccessor
};
sut.GetExample();
HttpResponse response = _httpContextAccessor.GetInstance().Response;
response.StatusCode.Should().Be(200);
using (var reader = new System.IO.StreamReader(response.OutputStream)) {
reader.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
string result = reader.ReadToEnd();
result.Should().Be("{\"text\":\"Hello World\"}");
}
}
For methods that return DTO directly:
When executing this skill for a user request, always provide: