How to Add an “APPROVED” Text Stamp to a PDF in C# Using PDF.co
In this tutorial, you will use C# and the PDF.co Web API to add an APPROVED text annotation to an existing PDF.
The application will:
- Send an existing invoice PDF to PDF.co.
- Add red
APPROVEDtext to its first page. - Download the edited PDF as
approved-invoice.pdf.
This workflow uses the PDF.co PDF Add endpoint:
POST /v1/pdf/edit/add
The endpoint can add text, images, links, form fields, signatures, and other content to existing PDFs. See the PDF.co PDF Add API documentation for all supported options.
Prerequisites
Before starting, make sure you have:
- A PDF.co account
- A PDF.co API key
- The .NET SDK
- A source PDF available through a public URL
This tutorial uses .NET 6 or later and does not require any third-party NuGet packages.
Create a C# Console Application
Open a terminal and create a new console application:
dotnet new console -n PdfCoAddText
cd PdfCoAddTextThe project includes a Program.cs file where you will add the sample code.
Configure the PDF.co API Key
Sign in to your PDF.co account and obtain your API key.
Store the key in an environment variable rather than placing it directly in the source code.
On macOS or Linux:
export PDFCO_API_KEY="YOUR_API_KEY"In Windows PowerShell:
$env:PDFCO_API_KEY="YOUR_API_KEY"Replace YOUR_API_KEY with your PDF.co API key.
Environment variables set this way apply to the current terminal session. Configure the variable through your deployment platform’s secret-management system when running the application in production.
Add the Complete C# Code
Replace the contents of Program.cs with the following code:
using System.Text;
using System.Text.Json;
const string endpoint = "https://api.pdf.co/v1/pdf/edit/add";
const string sourceFileUrl =
"https://pdfco-test-files.s3.us-west-2.amazonaws.com/pdf-edit/sample.pdf";
const string destinationFile = "approved-invoice.pdf";
string? apiKey = Environment.GetEnvironmentVariable("PDFCO_API_KEY");
if (string.IsNullOrWhiteSpace(apiKey))
{
throw new InvalidOperationException(
"Set the PDFCO_API_KEY environment variable before running the application.");
}
var requestBody = new
{
url = sourceFileUrl,
name = destinationFile,
password = "",
annotations = new[]
{
new
{
x = 400,
y = 600,
text = "APPROVED",
fontname = "Times New Roman",
size = 24,
color = "FF0000",
pages = "0"
}
}
};
string jsonPayload = JsonSerializer.Serialize(requestBody);
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("x-api-key", apiKey);
using var requestContent = new StringContent(
jsonPayload,
Encoding.UTF8,
"application/json");
Console.WriteLine("Sending the PDF to PDF.co...");
using HttpResponseMessage response = await httpClient.PostAsync(
endpoint,
requestContent);
string responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"PDF.co returned HTTP {(int)response.StatusCode}: {responseBody}");
}
using JsonDocument responseJson = JsonDocument.Parse(responseBody);
JsonElement root = responseJson.RootElement;
bool hasError =
root.TryGetProperty("error", out JsonElement errorProperty) &&
errorProperty.GetBoolean();
if (hasError)
{
string message =
root.TryGetProperty("message", out JsonElement messageProperty)
? messageProperty.GetString() ?? "Unknown PDF.co error."
: "Unknown PDF.co error.";
throw new InvalidOperationException(message);
}
string resultFileUrl =
root.GetProperty("url").GetString()
?? throw new InvalidOperationException(
"PDF.co did not return an output file URL.");
Console.WriteLine("Downloading the edited PDF...");
byte[] resultFile = await httpClient.GetByteArrayAsync(resultFileUrl);
await File.WriteAllBytesAsync(destinationFile, resultFile);
Console.WriteLine(
$"The edited PDF was saved as \"{Path.GetFullPath(destinationFile)}\".");Understand the Request
The application sends the following information to PDF.co:
var requestBody = new
{
url = sourceFileUrl,
name = destinationFile,
password = "",
annotations = new[]
{
new
{
x = 400,
y = 600,
text = "APPROVED",
fontname = "Times New Roman",
size = 24,
color = "FF0000",
pages = "0"
}
}
};Source PDF
The url property contains a direct URL to the existing PDF:
url = sourceFileUrlThe URL must be accessible to PDF.co. If the PDF is stored locally, upload it to PDF.co or another accessible storage service first.
Output Filename
The name property defines the generated filename:
name = destinationFileIn this example, the result is saved as:
approved-invoice.pdf
Text Annotation
The object inside annotations controls the text added to the PDF:
- x: Horizontal position measured from the left side of the page.
- y: Vertical position measured from the top of the page.
- text: Text to add.
- fontname: Font used for the annotation.
- size: Font size in points.
- color: Text color in hexadecimal
RRGGBBformat. - pages: Zero-based pages on which the text will appear.
The example uses:
text = "APPROVED"and the color:
color = "FF0000"FF0000 represents red.
The page value:
pages = "0"adds the text to the first page.
To place it on every page, use:
pages = "0-"Adjust the Text Position
PDF coordinates begin at the upper-left corner.
- Increasing
xmoves the text to the right. - Increasing
ymoves the text downward.
The example places the annotation at:
x = 400,
y = 600Because PDF documents can have different page dimensions, adjust these values for your source document.
You can use the PDF Edit Add Helper available through PDF.co to inspect a document and determine suitable coordinates.
Run the Application
Run the project:
dotnet runThe application will:
- Read the API key from the environment.
- send the source PDF URL and annotation settings to PDF.co.
- Check the API response for errors.
- Download the edited PDF.
- Save it in the project directory.
A successful run displays output similar to:
Sending the PDF to PDF.co...
Downloading the edited PDF...
The edited PDF was saved as "/path/to/project/approved-invoice.pdf".Review the Output
Open approved-invoice.pdf.
The result should contain the original sample invoice with the word APPROVED added in red on the first page.
The output is not a blank PDF created from scratch. It is an edited copy of the source invoice.
Confirm that:
- The original invoice content remains visible.
APPROVEDappears on the first page.- The text is red.
- The text uses the requested font and size.
- The text appears at the intended coordinates.
Using Newtonsoft.Json in an Existing Project
The sample above uses the built-in System.Text.Json library. If an existing application still uses Newtonsoft.Json and checks the response with ToObject, the type argument must be provided.
Use:
if (json["error"]?.ToObject<bool>() == false)
{
// Process the successful result.
}The following does not compile because C# cannot determine the intended return type:
json["error"].ToObject()
It produces compiler error CS0411.
Customization Examples
Change the Annotation Text
text = "REVIEWED"
Change the Text Color to Blue
color = "0000FF"
Increase the Font Size
size = 36
Add the Text to Every Page
pages = "0-"
Add the Text to Selected Pages
The following adds the annotation to the first, third, and fourth pages:
pages = "0,2-3"
PDF.co page numbering begins at zero.
Use a Password-Protected Source PDF
Provide the source document password:
password = "SOURCE_PDF_PASSWORD"
Do not hard-code sensitive passwords in a production application. Retrieve them through an environment variable or secret-management service.
Troubleshooting
The Application Reports a Missing API Key
Set the PDFCO_API_KEY environment variable in the same terminal session used to run the application.
PDF.co Cannot Access the Source File
Confirm that the source URL:
- Points directly to the PDF.
- Uses HTTPS.
- Is publicly accessible.
- Does not require an interactive login.
- Has not expired.
The Text Does Not Appear
Check the page selection and coordinates. The annotation may have been placed outside the visible page area.
Start with smaller x and y values to position the text closer to the upper-left corner.
The Text Appears on the Wrong Page
Remember that PDF.co uses zero-based page numbering. The first page is 0, the second page is 1, and so on.
The Application Receives a Timeout
For large documents or more demanding operations, use asynchronous processing. PDF.co will return a job ID that can be monitored with the Job Check endpoint.
The Output URL Expires
PDF.co output URLs use temporary storage. This application downloads the result immediately so the edited PDF remains available locally after the temporary URL expires.
Conclusion
In this tutorial, you used C# and the PDF.co PDF Add endpoint to place an APPROVED text annotation on an existing invoice PDF.
You also learned how to:
- Authenticate PDF.co requests securely.
- Build the PDF Add request with
System.Text.Json. - Select the target page.
- Control the text’s position, font, size, and color.
- Handle unsuccessful API responses.
- Download the edited PDF to local storage.
This same endpoint can be extended to add other text, images, links, signatures, form fields, or PDF content to existing documents.
Related Tutorials

