> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apitally.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Masking

> Mask sensitive data with the Apitally SDK for .NET.

The Apitally SDK provides mechanisms for masking sensitive data in captured requests, responses, and application logs.

## Default masking

The SDK automatically masks common sensitive query parameters, headers, and request/response body fields based on built-in patterns. For example, fields named `password`, `token`, `secret`, or headers like `Authorization` are masked by default.

See the [data privacy](/data-privacy#data-masking) page for more information about default masking.

## Custom masking

You can extend the default masking rules by providing additional regular expressions via the `MaskQueryParams`, `MaskHeaders`, and `MaskBodyFields` options. Patterns are case-insensitive and match anywhere within the name. Use `^` and `$` anchors for exact matches.

Body field patterns recursively match keys in JSON objects and replace the corresponding values only when they are strings.

```csharp Program.cs {9-12} theme={null}
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddApitally(options =>
{
    options.WriteToken = "your-write-token";
    options.CaptureRequestHeaders = true;
    options.CaptureRequestBody = true;
    options.CaptureResponseBody = true;
    // Mask specific query parameters, headers and body fields
    options.MaskQueryParams = ["^card_number$", "^account_id$"];
    options.MaskHeaders = ["^X-Custom-Key$", "^X-Internal-"];
    options.MaskBodyFields = ["^credit_card$", "social_security"];
});
```

### Body masking callbacks

For more control over body masking, you can provide callback functions via the `MaskRequestBody` and `MaskResponseBody` options. Each function receives the ended request `SpanSnapshot` and the captured body as a `byte[]`. Request metadata is available through [`span.Attributes`](/sdk-reference/dotnet/v1/attributes). Each function should return the masked body as a `byte[]`, or `null` to mask the entire body.

```csharp Callback function examples {61-63} theme={null}
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Apitally;

static byte[]? MaskRequestBody(SpanSnapshot span, byte[] body)
{
    var path = span.Attributes.GetValueOrDefault("url.path") as string ?? "";
    // Mask entire request body for admin endpoints
    if (path.StartsWith("/admin/"))
    {
        return null;
    }
    // Otherwise, return the original request body
    return body;
}

static byte[]? MaskResponseBody(SpanSnapshot span, byte[] body)
{
    var path = span.Attributes.GetValueOrDefault("url.path") as string ?? "";
    // Mask entire response body for admin endpoints
    if (path.StartsWith("/admin/"))
    {
        return null;
    }
    // Mask specific fields in user profile responses
    if (path.StartsWith("/users/"))
    {
        try
        {
            if (JsonNode.Parse(body) is JsonObject data)
            {
                if (data.ContainsKey("email"))
                {
                    data["email"] = "******";
                }
                if (data.ContainsKey("phone"))
                {
                    data["phone"] = "******";
                }
                return Encoding.UTF8.GetBytes(data.ToJsonString());
            }
        }
        catch (JsonException)
        {
            // Return the original body if parsing fails
        }
    }
    // Otherwise, return the original response body
    return body;
}

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddApitally(options =>
{
    options.WriteToken = "your-write-token";
    options.CaptureRequestHeaders = true;
    options.CaptureRequestBody = true;
    options.CaptureResponseBody = true;
    // Mask request and response bodies using custom logic
    options.MaskRequestBody = MaskRequestBody;
    options.MaskResponseBody = MaskResponseBody;
});
```

<Note>
  Callbacks are applied before pattern-based field masking. If the returned body contains JSON, it is still masked using the default and custom `MaskBodyFields` patterns.
</Note>

### Log record masking callback

To mask sensitive data in application logs, provide a callback function via the `MaskLogRecord` option. The function receives a `LogRecordSnapshot` with the `Timestamp`, `CategoryName`, `LogLevel`, `EventId`, and `Body` of the log record. You can modify the log message through `record.Body`. Return the same record to keep it, or `null` to drop it.

```csharp Callback function example {24} theme={null}
using System.Text.RegularExpressions;
using Apitally;

static LogRecordSnapshot? MaskLogRecord(LogRecordSnapshot record)
{
    // Drop logs from Entity Framework Core
    if (record.CategoryName.StartsWith("Microsoft.EntityFrameworkCore"))
    {
        return null;
    }
    // Mask tokens
    if (record.Body is not null)
    {
        record.Body = Regex.Replace(record.Body, @"token=\S+", "token=******");
    }
    return record;
}

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddApitally(options =>
{
    options.WriteToken = "your-write-token";
    options.MaskLogRecord = MaskLogRecord;
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.