From 8828137bb62824847ab13e7caf087c3e94b8e2fd Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 1 Sep 2026 11:42:58 +0200 Subject: [PATCH 1/9] Add Go and OpenTofu code samples to email templates Extends the sample conventions in CLAUDE.md with the two new tabs (Go as entry 8, OpenTofu as entry 9) and applies them to the smallest spec as a render canary: 5 Go samples and 4 OpenTofu ones. lang: hcl with label: OpenTofu, because GitBook highlights with Prism, which has an hcl component and no terraform one. Unsupported operations get no entry at all rather than a limitation comment, so the templates list operation has no OpenTofu tab (the provider has no list data source). Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 37 ++++++ specs/templates.openapi.yml | 234 ++++++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index b8d5925..f342ed8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -148,6 +148,8 @@ Include code samples in this order: 5. Ruby 6. .NET (C#) 7. Java +8. Go (`lang: go`, `label: Go`) +9. OpenTofu (`lang: hcl`, `label: OpenTofu`) - HCL, not an API call; see below ### Code Sample Format @@ -175,9 +177,42 @@ x-codeSamples: - **Use official Mailtrap SDKs** for language-specific examples - **If SDK doesn't support a method**, either let GitBook generate the example or add a comment noting SDK limitations +- **For Go and OpenTofu, omit the entry entirely** when the tool cannot express the operation. A tab that + says "unsupported" is worse than an absent tab, so `inbound.openapi.yml` gets no Go or OpenTofu samples + at all, and the tracking opt-out operations keep their cURL-only sample. - **Use environment variables** for API keys (e.g., `process.env.MAILTRAP_API_KEY`) - **Use Context7 MCP** to query SDK capabilities when unsure +#### Go samples + +- Full compilable `package main` programs: Go has no top-level-statement form, and a complete program can + be compile-checked, which is the point. +- Read the token from `os.Getenv("MAILTRAP_API_TOKEN")`, the idiom used by every example in + `mailtrap/mailtrap-go`. +- Every method takes a `context.Context` first and returns `(payload, *mailtrap.Response, error)`; discard + the response as `_` and handle errors as `if err != nil { log.Fatal(err) }`. +- Set optional pointer fields with `mailtrap.Ptr(...)`, and prefer the SDK's exported constants + (`mailtrap.WebhookTypeEmailSending`, `mailtrap.SendingStreamTransactional`, ...) over string literals. +- Target the latest **released** SDK version. Verify a sample by extracting it to its own package and + running `gofmt -l`, `go vet` and `go build` against that version - not by reading it. +- `gofmt` indents with tabs. Tabs are legal inside a YAML block scalar's content (only block + *indentation* must be spaces), so a `source: |` block indented with 12 spaces followed by tabs + round-trips correctly. + +#### OpenTofu samples + +- Use `lang: hcl` with `label: OpenTofu`. GitBook highlights with Prism, which has an `hcl` component and + no `terraform` one; `lang` resolves through Prism identifiers rather than GitBook's documented linguist + list, as the existing `csharp` and `shell` samples show. The dropdown tab is titled by `label`, so it + reads "OpenTofu" regardless. +- An HCL block is not an API call: a `resource` block declares desired state and the endpoint fires as a + side effect of a lifecycle command. Every sample therefore opens with a comment naming both the command + and the operation, e.g. `# tofu apply creates the domain: POST /api/domains`. +- Each sample is self-contained, including the `terraform { required_providers { ... } }` and + `provider "mailtrap" {}` blocks, because the sample tab is where a reader learns the source string. +- Take attribute names from the provider schema, not from its published examples. Verify with + `tofu fmt -check` plus `tofu validate` against a locally built provider binary under `dev_overrides`. + ### SDK Repositories Reference SDK repos for accurate code examples: @@ -187,6 +222,8 @@ Reference SDK repos for accurate code examples: - Ruby: `railsware/mailtrap-ruby` - .NET: Future reference - Java: Future reference +- Go: `mailtrap/mailtrap-go` +- OpenTofu/Terraform provider: `mailtrap/terraform-provider-mailtrap` ## OpenAPI Extensions diff --git a/specs/templates.openapi.yml b/specs/templates.openapi.yml index 859cd2c..6315eb5 100644 --- a/specs/templates.openapi.yml +++ b/specs/templates.openapi.yml @@ -107,6 +107,35 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.emailTemplatesApi().emailTemplates().getAllTemplates(accountId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + templates, _, err := client.EmailTemplates.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, template := range templates { + fmt.Printf("%d %s (%s)\n", template.ID, template.Name, template.Category) + } + } responses: '200': description: OK. @@ -246,6 +275,61 @@ paths: "Welcome {{user_name}}!", "

Welcome {{user_name}}!

") ); var template = client.emailTemplatesApi().emailTemplates().createEmailTemplate(accountId, request); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + template, _, err := client.EmailTemplates.Create(context.Background(), &mailtrap.EmailTemplateRequest{ + Name: "Welcome email", + Category: "Onboarding", + Subject: "Welcome to Mailtrap", + BodyHTML: "

Welcome, {{name}}!

", + BodyText: "Welcome, {{name}}!", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created template %d (uuid %s)\n", template.ID, template.UUID) + } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the template: POST /api/email_templates + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_email_template" "welcome" { + name = "Welcome email" + category = "Onboarding" + subject = "Welcome to Mailtrap" + body_html = "

Welcome, {{name}}!

" + body_text = "Welcome, {{name}}!" + } requestBody: content: application/json: @@ -357,6 +441,54 @@ paths: var template = client.emailTemplatesApi().emailTemplates() .getEmailTemplate(accountId, templateId); System.out.println(template); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + template, _, err := client.EmailTemplates.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s: %s\n", template.Name, template.Subject) + } + - lang: hcl + label: OpenTofu + source: | + # tofu plan refreshes the resource, and tofu import mailtrap_email_template.welcome 1000001 + # adopts an existing one: GET /api/email_templates/{email_template_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_email_template" "welcome" { + name = "Welcome email" + category = "Onboarding" + subject = "Welcome to Mailtrap" + } responses: '200': description: Returns attributes of the email template. @@ -496,6 +628,61 @@ paths: var template = client.emailTemplatesApi().emailTemplates() .updateEmailTemplate(accountId, templateId, request); System.out.println(template); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Only the fields you set are changed. A body that has been set cannot be + // cleared, only replaced. + template, _, err := client.EmailTemplates.Update(context.Background(), 1000001, &mailtrap.EmailTemplateRequest{ + Subject: "Welcome aboard", + BodyHTML: "

Welcome aboard, {{name}}!

", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("updated template %d: %s\n", template.ID, template.Subject) + } + - lang: hcl + label: OpenTofu + source: | + # changing a tracked attribute and running tofu apply: PATCH /api/email_templates/{email_template_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # The API cannot clear body_html or body_text once set: a body can only be + # replaced, so removing the argument leaves the stored value in place. + resource "mailtrap_email_template" "welcome" { + name = "Welcome email" + category = "Onboarding" + subject = "Welcome aboard" + body_html = "

Welcome aboard, {{name}}!

" + } requestBody: content: application/json: @@ -603,6 +790,53 @@ paths: long accountId = 1000001L; long templateId = 12345L; client.emailTemplatesApi().emailTemplates().deleteEmailTemplate(accountId, templateId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.EmailTemplates.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("template deleted") + } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/email_templates/{email_template_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_email_template" "welcome" { + name = "Welcome email" + category = "Onboarding" + subject = "Welcome to Mailtrap" + } responses: '204': description: No Content From e95f40affe23829c702a73b6fad25e56a0a3014b Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 1 Sep 2026 11:45:01 +0200 Subject: [PATCH 2/9] Add Go code samples to the Email Sandbox endpoints 32 samples: all 30 operations in sandbox.openapi.yml plus the two sandbox-sending ones. The sandbox sending client is constructed with WithSandbox(true) and WithSandboxID(...), which is what routes Send and SendBatch to the sandbox host. The five body.* operations return raw bytes, so those samples print string(body). The message list sample also shows the SandboxMessages.All iterator, which is the idiomatic way to walk every page. Co-Authored-By: Claude Opus 5 (1M context) --- specs/sandbox-sending.openapi.yml | 73 +++ specs/sandbox.openapi.yml | 853 ++++++++++++++++++++++++++++++ 2 files changed, 926 insertions(+) diff --git a/specs/sandbox-sending.openapi.yml b/specs/sandbox-sending.openapi.yml index c6bc9b5..b4dc973 100644 --- a/specs/sandbox-sending.openapi.yml +++ b/specs/sandbox-sending.openapi.yml @@ -186,6 +186,39 @@ paths: .build(); client.testingApi().emails().send(mail, config.getInboxId()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN"), mailtrap.WithSandbox(true), mailtrap.WithSandboxID(3000001)) + if err != nil { + log.Fatal(err) + } + + response, _, err := client.Send(context.Background(), &mailtrap.SendRequest{ + From: mailtrap.Address{Email: "sender@example.com", Name: "Mailtrap Test"}, + To: []mailtrap.Address{{Email: "recipient@example.com"}}, + Subject: "You are awesome!", + Text: "Congrats for sending test email with Mailtrap!", + Category: "Integration Test", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("captured by the sandbox: %v\n", response.MessageIDs) + } requestBody: content: application/json: @@ -460,6 +493,46 @@ paths: .batchSend(batchMail, config.getInboxId()); System.out.println(response); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN"), mailtrap.WithSandbox(true), mailtrap.WithSandboxID(3000001)) + if err != nil { + log.Fatal(err) + } + + // Base holds the fields shared by every message; per-request fields override it. + response, _, err := client.SendBatch(context.Background(), &mailtrap.BatchSendRequest{ + Base: &mailtrap.SendRequest{ + From: mailtrap.Address{Email: "sender@example.com", Name: "Mailtrap Test"}, + Subject: "You are awesome!", + Text: "Congrats for sending test email with Mailtrap!", + }, + Requests: []mailtrap.SendRequest{ + {To: []mailtrap.Address{{Email: "first@example.com"}}}, + {To: []mailtrap.Address{{Email: "second@example.com"}}}, + }, + }) + if err != nil { + log.Fatal(err) + } + + for i, item := range response.Responses { + fmt.Printf("message %d: success=%t ids=%v errors=%v\n", i, item.Success, item.MessageIDs, item.Errors) + } + } requestBody: content: application/json: diff --git a/specs/sandbox.openapi.yml b/specs/sandbox.openapi.yml index dfe05bd..0536627 100644 --- a/specs/sandbox.openapi.yml +++ b/specs/sandbox.openapi.yml @@ -161,6 +161,35 @@ paths: .getInboxes(accountId); System.out.println(inboxes); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandboxes, _, err := client.Sandboxes.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, sandbox := range sandboxes { + fmt.Printf("%d %s (%d message(s), %d unread)\n", sandbox.ID, sandbox.Name, sandbox.EmailsCount, sandbox.EmailsUnreadCount) + } + } operationId: getSandboxes '/api/projects': post: @@ -303,6 +332,33 @@ paths: .createProject(accountId, request); System.out.println(project); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + project, _, err := client.Projects.Create(context.Background(), "My project") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created project %d (%s)\n", project.ID, project.Name) + } get: summary: Get a list of projects description: List projects and their sandboxes to which the API token has access. @@ -419,6 +475,35 @@ paths: System.out.println("Project: " + project.getName() + ", ID: " + project.getId()); } + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + projects, _, err := client.Projects.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, project := range projects { + fmt.Printf("%d %s (%d sandbox(es))\n", project.ID, project.Name, len(project.Sandboxes)) + } + } '/api/projects/{project_id}': get: summary: Get project by ID @@ -539,6 +624,33 @@ paths: .getProject(accountId, projectId); System.out.println("Project: " + project.getName()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + project, _, err := client.Projects.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%d %s\n", project.ID, project.Name) + } patch: summary: Update project description: Update project name. The project name is min 2 characters and max 100 characters long. @@ -671,6 +783,33 @@ paths: .updateProject(accountId, projectId, request); System.out.println("Project updated: " + updatedProject.getName()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + project, _, err := client.Projects.Update(context.Background(), 1000001, "Renamed project") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("renamed project %d to %s\n", project.ID, project.Name) + } requestBody: content: application/json: @@ -803,6 +942,32 @@ paths: .deleteProject(accountId, projectId); System.out.println("Project deleted: " + deletedProject.getId()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.Projects.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("project deleted") + } responses: '200': description: Returns id of the deleted project. @@ -977,6 +1142,33 @@ paths: System.out.println("Inbox created: " + inbox.getName()); System.out.println("SMTP credentials - User: " + inbox.getUsername()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.Create(context.Background(), 1000001, "Staging inbox") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created sandbox %d (%s)\n", sandbox.ID, sandbox.Name) + } parameters: - $ref: '#/components/parameters/project_id' '/api/sandboxes/{sandbox_id}': @@ -1092,6 +1284,33 @@ paths: .getInboxAttributes(accountId, inboxId); System.out.println(inboxAttributes); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.Get(context.Background(), 3000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s: %s@%s, SMTP %v\n", sandbox.Name, sandbox.Username, sandbox.Domain, sandbox.SMTPPorts) + } operationId: getSandboxAttributes delete: summary: Delete a sandbox @@ -1203,6 +1422,34 @@ paths: .deleteInbox(ACCOUNT_ID, inboxId); System.out.println(deletedInbox); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Unlike most deletes, this one returns the deleted sandbox. + sandbox, _, err := client.Sandboxes.Delete(context.Background(), 3000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("deleted sandbox %d (%s)\n", sandbox.ID, sandbox.Name) + } operationId: deleteSandbox patch: summary: Update a sandbox @@ -1361,6 +1608,36 @@ paths: .updateInbox(accountId, inboxId, updateRequest); System.out.println(updatedInbox); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.Update(context.Background(), 3000001, &mailtrap.SandboxUpdateRequest{ + Name: "Renamed inbox", + EmailUsername: "staging", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("updated sandbox %d: %s\n", sandbox.ID, sandbox.Name) + } operationId: updateSandbox parameters: - $ref: '#/components/parameters/sandbox_id' @@ -1477,6 +1754,33 @@ paths: .cleanInbox(accountId, inboxId); System.out.println(cleanedInbox); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.Clean(context.Background(), 3000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("sandbox %d now holds %d message(s)\n", sandbox.ID, sandbox.EmailsCount) + } operationId: cleanSandbox parameters: - $ref: '#/components/parameters/sandbox_id' @@ -1593,6 +1897,33 @@ paths: .markAsRead(accountId, inboxId); System.out.println(markedInbox); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.MarkAllRead(context.Background(), 3000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("sandbox %d has %d unread message(s)\n", sandbox.ID, sandbox.EmailsUnreadCount) + } operationId: markAsReadSandbox parameters: - $ref: '#/components/parameters/sandbox_id' @@ -1709,6 +2040,33 @@ paths: .resetCredentials(accountId, inboxId); System.out.println(updatedInbox); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.ResetCredentials(context.Background(), 3000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("new SMTP credentials: %s / %s\n", sandbox.Username, sandbox.Password) + } operationId: resetSandboxCredentials parameters: - $ref: '#/components/parameters/sandbox_id' @@ -1827,6 +2185,33 @@ paths: .enableEmailAddress(accountId, inboxId); System.out.println(updatedInbox); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.ToggleEmailAddress(context.Background(), 3000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("email address enabled: %t (%s@%s)\n", sandbox.EmailUsernameEnabled, sandbox.EmailUsername, sandbox.EmailDomain) + } operationId: enableSandboxEmailAddresses parameters: - $ref: '#/components/parameters/sandbox_id' @@ -1946,6 +2331,33 @@ paths: .resetEmailAddresses(accountId, inboxId); System.out.println(updatedInbox); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + sandbox, _, err := client.Sandboxes.ResetEmailAddress(context.Background(), 3000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("email address reset to %s@%s\n", sandbox.EmailUsername, sandbox.EmailDomain) + } operationId: resetEmailUserNamePerSandbox parameters: - $ref: '#/components/parameters/sandbox_id' @@ -2118,6 +2530,33 @@ paths: .getMessage(accountId, inboxId, messageId); System.out.println(message); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + message, _, err := client.SandboxMessages.Get(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s -> %s: %s (read: %t)\n", message.FromEmail, message.ToEmail, message.Subject, message.IsRead) + } operationId: showSandboxEmailMessage patch: summary: Update message @@ -2367,6 +2806,33 @@ paths: .updateMessage(accountId, inboxId, messageId, updateRequest); System.out.println(updatedMessage); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + message, _, err := client.SandboxMessages.Update(context.Background(), 3000001, 4000001, true) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("message %d read: %t\n", message.ID, message.IsRead) + } operationId: updateSandboxEmailMessage delete: summary: Delete message @@ -2576,6 +3042,34 @@ paths: .deleteMessage(accountId, inboxId, messageId); System.out.println(deletedMessage); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Delete returns the message it removed. + message, _, err := client.SandboxMessages.Delete(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("deleted message %d (%s)\n", message.ID, message.Subject) + } operationId: deleteSandboxEmailMessage parameters: - $ref: '#/components/parameters/sandbox_id' @@ -2758,6 +3252,47 @@ paths: System.out.println("Subject: " + message.getSubject()); System.out.println("Received: " + message.getCreatedAt()); } + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + messages, _, err := client.SandboxMessages.List(context.Background(), 3000001, &mailtrap.MessageListOptions{ + Search: "welcome", + Page: 1, + }) + if err != nil { + log.Fatal(err) + } + + for _, message := range messages { + fmt.Printf("%d %s (from %s)\n", message.ID, message.Subject, message.FromEmail) + } + + // To walk every page instead of one, use the iterator: + // + // for message, err := range client.SandboxMessages.All(context.Background(), 3000001, nil) { + // if err != nil { + // log.Fatal(err) + // } + // fmt.Println(message.Subject) + // } + } parameters: - schema: type: string @@ -2945,6 +3480,33 @@ paths: .forwardMessage(accountId, inboxId, messageId, forwardRequest); System.out.println(forwardedMessage); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The recipient must have confirmed forwarding to this address in advance. + if _, err := client.SandboxMessages.Forward(context.Background(), 3000001, 4000001, "qa@example.com"); err != nil { + log.Fatal(err) + } + + fmt.Println("message forwarded") + } operationId: forwardSandboxEmailMessage parameters: - $ref: '#/components/parameters/sandbox_id' @@ -3105,6 +3667,36 @@ paths: var spamReport = client.testingApi().messages().getSpamScore(accountId, inboxId, messageId); System.out.println(spamReport); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + report, _, err := client.SandboxMessages.SpamReport(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("score %.2f of %.2f, spam: %t\n", report.Score, report.Threshold, report.Spam) + for _, detail := range report.Details { + fmt.Printf(" %+.2f %s: %s\n", detail.Pts, detail.RuleName, detail.Description) + } + } operationId: getSandboxEmailMessageSpamReport parameters: - $ref: '#/components/parameters/sandbox_id' @@ -3285,6 +3877,35 @@ paths: long inboxId = 12345L; var htmlAnalysis = client.testingApi().messages().getMessageHtmlAnalysis(accountId, inboxId, messageId); System.out.println(htmlAnalysis); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + report, _, err := client.SandboxMessages.HTMLAnalysis(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + for _, issue := range report.Errors { + fmt.Printf("line %d: %s (desktop: %v)\n", issue.ErrorLine, issue.RuleName, issue.EmailClients.Desktop) + } + } operationId: getSandboxEmailMessageHTMLAnalysis parameters: - $ref: '#/components/parameters/sandbox_id' @@ -3416,6 +4037,35 @@ paths: String textBody = client.testingApi().messages().getTextMessage(accountId, inboxId, messageId); System.out.println(textBody); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The body endpoints return the raw bytes. + body, _, err := client.SandboxMessages.Text(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + // body holds the plain-text body. + fmt.Println(string(body)) + } operationId: getSandboxEmailMessageBodyAsTxt parameters: - $ref: '#/components/parameters/sandbox_id' @@ -3591,6 +4241,35 @@ paths: String rawBody = client.testingApi().messages().getRawMessage(accountId, inboxId, messageId); System.out.println(rawBody); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The body endpoints return the raw bytes. + body, _, err := client.SandboxMessages.Raw(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + // body holds the raw MIME source. + fmt.Println(string(body)) + } operationId: getSandboxEmailMessageBodyAsRaw parameters: - $ref: '#/components/parameters/sandbox_id' @@ -3741,6 +4420,35 @@ paths: String htmlSource = client.testingApi().messages().getMessageSource(accountId, inboxId, messageId); System.out.println(htmlSource); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The body endpoints return the raw bytes. + body, _, err := client.SandboxMessages.HTMLSource(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + // body holds the HTML source. + fmt.Println(string(body)) + } operationId: getSandboxEmailMessageBodyAsHtmlSource parameters: - $ref: '#/components/parameters/sandbox_id' @@ -3894,6 +4602,35 @@ paths: long messageId = 12345L; String htmlBody = client.testingApi().messages().getHtmlMessage(accountId, inboxId, messageId); System.out.println(htmlBody); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The body endpoints return the raw bytes. + body, _, err := client.SandboxMessages.HTML(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + // body holds the formatted HTML body. + fmt.Println(string(body)) + } operationId: getSandboxEmailMessageBodyAsHtml parameters: - $ref: '#/components/parameters/sandbox_id' @@ -4070,6 +4807,35 @@ paths: String emlContent = client.testingApi().messages().getMessageAsEml(accountId, inboxId, messageId); System.out.println(emlContent); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The body endpoints return the raw bytes. + body, _, err := client.SandboxMessages.EML(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + // body holds the message in .eml format. + fmt.Println(string(body)) + } operationId: getSandboxEmailMessageBodyAsEml parameters: - $ref: '#/components/parameters/sandbox_id' @@ -4196,6 +4962,35 @@ paths: var mailHeaders = client.testingApi().messages().getMailHeaders(accountId, inboxId, messageId); System.out.println(mailHeaders); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + headers, _, err := client.SandboxMessages.Headers(context.Background(), 3000001, 4000001) + if err != nil { + log.Fatal(err) + } + + for name, value := range headers { + fmt.Printf("%s: %s\n", name, value) + } + } operationId: getMailHeadersOfEmailMessage parameters: - $ref: '#/components/parameters/sandbox_id' @@ -4361,6 +5156,37 @@ paths: long messageId = 67890L; var attachments = client.testingApi().attachments().getAttachments(accountId, inboxId, messageId, null); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + attachments, _, err := client.SandboxAttachments.List(context.Background(), 3000001, 4000001, &mailtrap.AttachmentListOptions{ + Type: "attachment", + }) + if err != nil { + log.Fatal(err) + } + + for _, attachment := range attachments { + fmt.Printf("%d %s (%s, %s)\n", attachment.ID, attachment.Filename, attachment.ContentType, attachment.AttachmentHumanSize) + } + } parameters: - $ref: '#/components/parameters/sandbox_id' - $ref: '#/components/parameters/message_id' @@ -4518,6 +5344,33 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var attachment = client.testingApi().attachments().getSingleAttachment(accountId, inboxId, messageId, attachmentId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + attachment, _, err := client.SandboxAttachments.Get(context.Background(), 3000001, 4000001, 5000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s (%s): %s\n", attachment.Filename, attachment.ContentType, attachment.DownloadPath) + } parameters: - $ref: '#/components/parameters/sandbox_id' - $ref: '#/components/parameters/message_id' From cca8895af9fc57e06446afb7862724e008fd30c5 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 1 Sep 2026 11:47:20 +0200 Subject: [PATCH 3/9] Add Go code samples to the Email Sending endpoints 28 samples: 24 of the 27 operations in email-sending.openapi.yml, plus the two bulk and two transactional sending operations. Host selection is a client option, so the bulk samples construct the client with WithBulk(true) and the transactional ones use the default. The three tracking opt-out operations get no Go sample: the SDK has no support for them, and an absent tab is better than one that says so. Go lands on five operations the other SDKs still skip (updateDomain, the three company_info operations, and createSuppression), so those tabs will show cURL, Ruby and Go only until the other languages are back-filled. Co-Authored-By: Claude Opus 5 (1M context) --- specs/email-sending-bulk.openapi.yml | 75 ++ specs/email-sending-transactional.openapi.yml | 75 ++ specs/email-sending.openapi.yml | 746 ++++++++++++++++++ 3 files changed, 896 insertions(+) diff --git a/specs/email-sending-bulk.openapi.yml b/specs/email-sending-bulk.openapi.yml index 2cc0b73..8a4ca17 100644 --- a/specs/email-sending-bulk.openapi.yml +++ b/specs/email-sending-bulk.openapi.yml @@ -212,6 +212,40 @@ paths: .build(); client.bulkSendingApi().emails().send(mail); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN"), mailtrap.WithBulk(true)) + if err != nil { + log.Fatal(err) + } + + // WithBulk(true) routes Send and SendBatch to the bulk host. + response, _, err := client.Send(context.Background(), &mailtrap.SendRequest{ + From: mailtrap.Address{Email: "sender@mail.example.com", Name: "Mailtrap Test"}, + To: []mailtrap.Address{{Email: "recipient@example.com"}}, + Subject: "You are awesome!", + Text: "Congrats for sending test email with Mailtrap!", + Category: "Bulk Test", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("sent: %v\n", response.MessageIDs) + } requestBody: required: true content: @@ -447,6 +481,47 @@ paths: .build(); client.bulkSendingApi().emails().batchSend(batchMail); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN"), mailtrap.WithBulk(true)) + if err != nil { + log.Fatal(err) + } + + // Base holds the fields shared by every message; per-request fields override it. + response, _, err := client.SendBatch(context.Background(), &mailtrap.BatchSendRequest{ + Base: &mailtrap.SendRequest{ + From: mailtrap.Address{Email: "sender@mail.example.com", Name: "Mailtrap Test"}, + Subject: "You are awesome!", + Text: "Congrats for sending test email with Mailtrap!", + Category: "Bulk Test", + }, + Requests: []mailtrap.SendRequest{ + {To: []mailtrap.Address{{Email: "first@example.com"}}}, + {To: []mailtrap.Address{{Email: "second@example.com"}}}, + }, + }) + if err != nil { + log.Fatal(err) + } + + for i, item := range response.Responses { + fmt.Printf("message %d: success=%t ids=%v errors=%v\n", i, item.Success, item.MessageIDs, item.Errors) + } + } requestBody: required: true content: diff --git a/specs/email-sending-transactional.openapi.yml b/specs/email-sending-transactional.openapi.yml index b554bf3..26523e6 100644 --- a/specs/email-sending-transactional.openapi.yml +++ b/specs/email-sending-transactional.openapi.yml @@ -182,6 +182,40 @@ paths: .build(); client.sendingApi().emails().send(mail); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The default client targets the transactional host. + response, _, err := client.Send(context.Background(), &mailtrap.SendRequest{ + From: mailtrap.Address{Email: "sender@mail.example.com", Name: "Mailtrap Test"}, + To: []mailtrap.Address{{Email: "recipient@example.com"}}, + Subject: "You are awesome!", + Text: "Congrats for sending test email with Mailtrap!", + Category: "Integration Test", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("sent: %v\n", response.MessageIDs) + } requestBody: required: true content: @@ -416,6 +450,47 @@ paths: .build(); client.sendingApi().emails().batchSend(batchMail); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Base holds the fields shared by every message; per-request fields override it. + response, _, err := client.SendBatch(context.Background(), &mailtrap.BatchSendRequest{ + Base: &mailtrap.SendRequest{ + From: mailtrap.Address{Email: "sender@mail.example.com", Name: "Mailtrap Test"}, + Subject: "You are awesome!", + Text: "Congrats for sending test email with Mailtrap!", + Category: "Integration Test", + }, + Requests: []mailtrap.SendRequest{ + {To: []mailtrap.Address{{Email: "first@example.com"}}}, + {To: []mailtrap.Address{{Email: "second@example.com"}}}, + }, + }) + if err != nil { + log.Fatal(err) + } + + for i, item := range response.Responses { + fmt.Printf("message %d: success=%t ids=%v errors=%v\n", i, item.Success, item.MessageIDs, item.Errors) + } + } requestBody: required: true content: diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index 4fffb5b..5ebd5b6 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -186,6 +186,36 @@ paths: .create(YOUR_ACCOUNT_ID, request); System.out.println(domain); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + domain, _, err := client.SendingDomains.Create(context.Background(), "mail.example.com") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created domain %d (%s)\n", domain.ID, domain.DomainName) + for _, record := range domain.DNSRecords { + fmt.Printf(" %s %s %q -> %q\n", record.Key, record.Type, record.Name, record.Value) + } + } requestBody: required: true content: @@ -324,6 +354,35 @@ paths: domains.forEach(domain -> System.out.println("Domain: " + domain.getDomainName()) ); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + domains, _, err := client.SendingDomains.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, domain := range domains { + fmt.Printf("%d %s (DNS verified: %t, compliance: %s)\n", domain.ID, domain.DomainName, domain.DNSVerified, domain.ComplianceStatus) + } + } responses: '200': $ref: '#/components/responses/DomainsResponse' @@ -436,6 +495,33 @@ paths: .getSendingDomain(YOUR_ACCOUNT_ID, domainId); System.out.println("Domain: " + domain.getDomainName()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + domain, _, err := client.SendingDomains.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s: DNS verified %t, open tracking %t\n", domain.DomainName, domain.DNSVerified, domain.OpenTrackingEnabled) + } parameters: - $ref: '#/components/parameters/domain_id' responses: @@ -548,6 +634,32 @@ paths: .deleteSendingDomain(YOUR_ACCOUNT_ID, domainId); System.out.println("Domain deleted successfully"); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.SendingDomains.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("domain deleted") + } parameters: - $ref: '#/components/parameters/domain_id' responses: @@ -599,6 +711,39 @@ paths: click_tracking_enabled: true, auto_unsubscribe_link_enabled: false ) + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The tracking flags are pointers: a nil field keeps its current value, so use + // mailtrap.Ptr to send an explicit true or false. + domain, _, err := client.SendingDomains.Update(context.Background(), 1000001, &mailtrap.UpdateDomainRequest{ + OpenTrackingEnabled: mailtrap.Ptr(true), + ClickTrackingEnabled: mailtrap.Ptr(true), + AutoUnsubscribeLinkEnabled: mailtrap.Ptr(false), + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s: open tracking %t, click tracking %t\n", domain.DomainName, domain.OpenTrackingEnabled, domain.ClickTrackingEnabled) + } parameters: - $ref: '#/components/parameters/domain_id' requestBody: @@ -759,6 +904,32 @@ paths: ); System.out.println("Setup instructions sent"); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.SendingDomains.SendSetupInstructions(context.Background(), 1000001, "devops@example.com"); err != nil { + log.Fatal(err) + } + + fmt.Println("setup instructions sent") + } parameters: - $ref: '#/components/parameters/domain_id' requestBody: @@ -813,6 +984,33 @@ paths: domain_id = 12345 puts company_info.get(domain_id) + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + info, _, err := client.SendingDomains.CompanyInfo(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s (%s), %s %s, %s\n", info.Name, info.InfoLevel, info.ZipCode, info.City, info.Country) + } parameters: - $ref: '#/components/parameters/domain_id' responses: @@ -874,6 +1072,41 @@ paths: website_url: 'https://mailtrap.io', info_level: 'business' ) + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + info, _, err := client.SendingDomains.CreateCompanyInfo(context.Background(), 1000001, &mailtrap.CompanyInfoRequest{ + Name: "Example Inc.", + Address: "1 Example Street", + City: "Dublin", + Country: "Ireland", + ZipCode: "D01 F5P2", + WebsiteURL: "https://example.com", + InfoLevel: mailtrap.InfoLevelBusiness, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("company info set for %s\n", info.Name) + } parameters: - $ref: '#/components/parameters/domain_id' requestBody: @@ -943,6 +1176,37 @@ paths: domain_id = 12345 puts company_info.update(domain_id, city: 'New York', zip_code: '10001') + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Only the fields you set are changed. + info, _, err := client.SendingDomains.UpdateCompanyInfo(context.Background(), 1000001, &mailtrap.CompanyInfoRequest{ + Phone: "+353 1 234 5678", + PrivacyPolicyURL: "https://example.com/privacy", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("company info updated for %s\n", info.Name) + } parameters: - $ref: '#/components/parameters/domain_id' requestBody: @@ -1130,6 +1394,38 @@ paths: filtered.forEach(filteredSuppression -> System.out.println("Email: " + filteredSuppression.getEmail()) ); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + suppressions, _, err := client.Suppressions.List(context.Background(), &mailtrap.SuppressionListOptions{ + StartTime: "2026-01-01T00:00:00Z", + EndTime: "2026-02-01T00:00:00Z", + }) + if err != nil { + log.Fatal(err) + } + + for _, suppression := range suppressions { + fmt.Printf("%s %s (%s, %s)\n", suppression.ID, suppression.Email, suppression.Type, suppression.SendingStream) + } + } parameters: - name: email in: query @@ -1196,6 +1492,38 @@ paths: "sending_stream": "transactional", "type": "manual import" }' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + suppression, _, err := client.Suppressions.Create(context.Background(), &mailtrap.CreateSuppressionRequest{ + Email: "bounced@example.com", + DomainID: 1000001, + SendingStream: mailtrap.SendingStreamTransactional, + Type: mailtrap.SuppressionTypeHardBounce, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("suppressed %s (%s)\n", suppression.Email, suppression.Type) + } requestBody: required: true content: @@ -1348,6 +1676,34 @@ paths: .deleteSuppression(YOUR_ACCOUNT_ID, suppressionId); System.out.println("Suppression removed: " + result.getEmail()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Delete returns the suppression it removed. + suppression, _, err := client.Suppressions.Delete(context.Background(), "018dd3e0-1d2f-7a4b-9c8e-3f5a6b7c8d9e") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("unsuppressed %s\n", suppression.Email) + } parameters: - $ref: '#/components/parameters/suppression_id' responses: @@ -1592,6 +1948,37 @@ paths: var filter = StatsFilter.builder().startDate("2026-01-01").endDate("2026-01-31").build(); var stats = client.sendingApi().stats().getStats(accountId, filter); System.out.println(stats); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // StartDate and EndDate are required; the other fields narrow the query. + stats, _, err := client.Stats.Get(context.Background(), &mailtrap.StatsOptions{ + StartDate: "2026-01-01", + EndDate: "2026-01-31", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("delivered %d (%.2f), opened %d (%.2f)\n", stats.DeliveryCount, stats.DeliveryRate, stats.OpenCount, stats.OpenRate) + } parameters: - $ref: '#/components/parameters/StartDateQueryFilter' - $ref: '#/components/parameters/EndDateQueryFilter' @@ -1698,6 +2085,39 @@ paths: var filter = StatsFilter.builder().startDate("2026-01-01").endDate("2026-01-31").build(); var stats = client.sendingApi().stats().byDomain(accountId, filter); System.out.println(stats); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + stats, _, err := client.Stats.ByDomain(context.Background(), &mailtrap.StatsOptions{ + StartDate: "2026-01-01", + EndDate: "2026-01-31", + SendingStreams: []string{mailtrap.SendingStreamTransactional}, + }) + if err != nil { + log.Fatal(err) + } + + for _, entry := range stats { + fmt.Printf("domain %d: delivered %d, bounced %d\n", entry.DomainID, entry.Stats.DeliveryCount, entry.Stats.BounceCount) + } + } parameters: - $ref: '#/components/parameters/StartDateQueryFilter' - $ref: '#/components/parameters/EndDateQueryFilter' @@ -1819,6 +2239,39 @@ paths: var filter = StatsFilter.builder().startDate("2026-01-01").endDate("2026-01-31").build(); var stats = client.sendingApi().stats().byCategory(accountId, filter); System.out.println(stats); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + stats, _, err := client.Stats.ByCategory(context.Background(), &mailtrap.StatsOptions{ + StartDate: "2026-01-01", + EndDate: "2026-01-31", + Categories: []string{"Password Reset"}, + }) + if err != nil { + log.Fatal(err) + } + + for _, entry := range stats { + fmt.Printf("%s: delivered %d, opened %d\n", entry.Category, entry.Stats.DeliveryCount, entry.Stats.OpenCount) + } + } parameters: - $ref: '#/components/parameters/StartDateQueryFilter' - $ref: '#/components/parameters/EndDateQueryFilter' @@ -1933,6 +2386,38 @@ paths: var filter = StatsFilter.builder().startDate("2026-01-01").endDate("2026-01-31").build(); var stats = client.sendingApi().stats().byEmailServiceProvider(accountId, filter); System.out.println(stats); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + stats, _, err := client.Stats.ByEmailServiceProvider(context.Background(), &mailtrap.StatsOptions{ + StartDate: "2026-01-01", + EndDate: "2026-01-31", + }) + if err != nil { + log.Fatal(err) + } + + for _, entry := range stats { + fmt.Printf("%s: delivered %d (%.2f)\n", entry.EmailServiceProvider, entry.Stats.DeliveryCount, entry.Stats.DeliveryRate) + } + } parameters: - $ref: '#/components/parameters/StartDateQueryFilter' - $ref: '#/components/parameters/EndDateQueryFilter' @@ -2047,6 +2532,39 @@ paths: var filter = StatsFilter.builder().startDate("2026-01-01").endDate("2026-01-31").build(); var stats = client.sendingApi().stats().byDate(accountId, filter); System.out.println(stats); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + stats, _, err := client.Stats.ByDate(context.Background(), &mailtrap.StatsOptions{ + StartDate: "2026-01-01", + EndDate: "2026-01-31", + SendingDomainIDs: []int64{1000001}, + }) + if err != nil { + log.Fatal(err) + } + + for _, entry := range stats { + fmt.Printf("%s: delivered %d, clicked %d\n", entry.Date, entry.Stats.DeliveryCount, entry.Stats.ClickCount) + } + } parameters: - $ref: '#/components/parameters/StartDateQueryFilter' - $ref: '#/components/parameters/EndDateQueryFilter' @@ -2163,6 +2681,50 @@ paths: long accountId = 1000001L; var response = client.sendingApi().emailLogs().list(accountId, null, null); System.out.println(response.getTotalCount()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + logs, _, err := client.EmailLogs.List(context.Background(), &mailtrap.EmailLogsListOptions{ + SentAfter: "2026-01-01T00:00:00Z", + Filters: map[string]mailtrap.LogFilter{ + "status": {Operator: "equal", Values: []string{mailtrap.EmailLogStatusDelivered}}, + }, + }) + if err != nil { + log.Fatal(err) + } + + for _, message := range logs.Messages { + fmt.Printf("%s %s -> %s (%s)\n", message.MessageID, message.From, message.To, message.Status) + } + + // To walk every page instead of one, use the iterator, which follows + // next_page_cursor for you: + // + // for message, err := range client.EmailLogs.All(context.Background(), nil) { + // if err != nil { + // log.Fatal(err) + // } + // fmt.Println(message.MessageID) + // } + } parameters: - name: search_after in: query @@ -2281,6 +2843,36 @@ paths: String messageId = "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d"; var message = client.sendingApi().emailLogs().get(accountId, messageId); System.out.println(message.getSubject()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + message, _, err := client.EmailLogs.Get(context.Background(), "018dd3e0-1d2f-7a4b-9c8e-3f5a6b7c8d9e") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s (%s), %d open(s)\n", message.Subject, message.Status, message.OpensCount) + for _, event := range message.Events { + fmt.Printf(" %s at %s\n", event.EventType, event.CreatedAt) + } + } parameters: - $ref: '#/components/parameters/sending_message_id' responses: @@ -2452,6 +3044,43 @@ paths: // signing_secret is returned only on creation — store it securely. var webhook = client.generalApi().webhooks().createWebhook(accountId, request); System.out.println(webhook); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The API requires sending_stream for email_sending webhooks. + webhook, _, err := client.Webhooks.Create(context.Background(), &mailtrap.CreateWebhookRequest{ + URL: "https://example.com/mailtrap/webhook", + WebhookType: mailtrap.WebhookTypeEmailSending, + SendingStream: mailtrap.SendingStreamTransactional, + PayloadFormat: mailtrap.PayloadFormatJSON, + DomainID: mailtrap.Ptr(int64(1000001)), + EventTypes: []string{mailtrap.WebhookEventDelivery, mailtrap.WebhookEventBounce, mailtrap.WebhookEventOpen}, + Active: mailtrap.Ptr(true), + }) + if err != nil { + log.Fatal(err) + } + + // The signing secret is returned only here: store it to verify payloads. + fmt.Printf("created webhook %d, signing secret %s\n", webhook.ID, webhook.SigningSecret) + } requestBody: required: true content: @@ -2642,6 +3271,35 @@ paths: long accountId = 1000001L; var webhooks = client.generalApi().webhooks().getAllWebhooks(accountId); System.out.println(webhooks); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + webhooks, _, err := client.Webhooks.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, webhook := range webhooks { + fmt.Printf("%d %s (%s, active: %t)\n", webhook.ID, webhook.URL, webhook.WebhookType, webhook.Active) + } + } responses: '200': description: List of webhooks @@ -2761,6 +3419,33 @@ paths: long webhookId = 123L; var webhook = client.generalApi().webhooks().getWebhook(accountId, webhookId); System.out.println(webhook); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + webhook, _, err := client.Webhooks.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s (%s): %v\n", webhook.URL, webhook.PayloadFormat, webhook.EventTypes) + } parameters: - $ref: '#/components/parameters/webhook_id' responses: @@ -2891,6 +3576,39 @@ paths: var request = new UpdateWebhookRequest(WebhookInput.builder().active(false).build()); var webhook = client.generalApi().webhooks().updateWebhook(accountId, webhookId, request); System.out.println(webhook); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Only url, active, payload_format and event_types are mutable. + webhook, _, err := client.Webhooks.Update(context.Background(), 1000001, &mailtrap.UpdateWebhookRequest{ + URL: "https://example.com/mailtrap/webhook/v2", + Active: mailtrap.Ptr(false), + PayloadFormat: mailtrap.PayloadFormatJSONLines, + EventTypes: []string{mailtrap.WebhookEventDelivery, mailtrap.WebhookEventSpamComplaint}, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("updated webhook %d: %s (active: %t)\n", webhook.ID, webhook.URL, webhook.Active) + } parameters: - $ref: '#/components/parameters/webhook_id' requestBody: @@ -3058,6 +3776,34 @@ paths: long accountId = 1000001L; long webhookId = 123L; client.generalApi().webhooks().deleteWebhook(accountId, webhookId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Delete returns the webhook it removed. + webhook, _, err := client.Webhooks.Delete(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("deleted webhook %d (%s)\n", webhook.ID, webhook.URL) + } parameters: - $ref: '#/components/parameters/webhook_id' responses: From 2b3922721ee018f03bdfb0daca790712d1d8f9b1 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 1 Sep 2026 11:48:58 +0200 Subject: [PATCH 4/9] Add Go code samples to the Contacts and Account Management endpoints 32 samples covering all 19 contacts operations and all 13 account-management ones. Contact identifiers are a UUID or an email address, and the contact update endpoint is an upsert, so that sample prints the action the API reports. The two sub-account samples construct the client with WithOrganizationID, which those endpoints require. The API token samples do not set an expiration, so they match what the API applies by default. Co-Authored-By: Claude Opus 5 (1M context) --- specs/account-management.openapi.yml | 397 +++++++++++++++++++ specs/contacts.openapi.yml | 562 +++++++++++++++++++++++++++ 2 files changed, 959 insertions(+) diff --git a/specs/account-management.openapi.yml b/specs/account-management.openapi.yml index 51237f7..891fb3b 100644 --- a/specs/account-management.openapi.yml +++ b/specs/account-management.openapi.yml @@ -135,6 +135,35 @@ paths: System.out.println("ID: " + account.getId()); System.out.println("Access Level: " + account.getAccessLevels().get(0)); } + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + accounts, _, err := client.Accounts.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, account := range accounts { + fmt.Printf("%d %s (access levels %v)\n", account.ID, account.Name, account.AccessLevels) + } + } responses: '200': description: 'Returns the list of accounts to which the API token has access. **access_levels** can return 1000 (account owner), 100 (admin), 10 (viewer).' @@ -252,6 +281,38 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.generalApi().accountAccesses().listUserAndInviteAccountAccesses(accountId, ListAccountAccessQueryParams.empty()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Pass nil for no filters. Requires account admin or owner permissions. + accesses, _, err := client.AccountAccesses.List(context.Background(), &mailtrap.AccountAccessListOptions{ + DomainIDs: []int64{1000001}, + }) + if err != nil { + log.Fatal(err) + } + + for _, access := range accesses { + fmt.Printf("%d %s (%s)\n", access.ID, access.Specifier.Email, access.SpecifierType) + } + } parameters: - name: project_ids description: The identifiers of the projects for which to include the results @@ -372,6 +433,32 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); client.generalApi().accountAccesses().removeAccountAccess(accessId, accountId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.AccountAccesses.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("account access removed") + } responses: '200': description: Returns confirmation of successful deletion and id of the deleted access. @@ -534,6 +621,46 @@ paths: )); client.generalApi().permissions().managePermissions(accessId, accountId, request); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // A resource_type/resource_id pair that already exists is updated, otherwise it + // is created. Set Destroy to remove a permission instead. + _, err = client.Permissions.BulkUpdate(context.Background(), 1000001, []*mailtrap.PermissionUpdate{ + { + ResourceID: "3000001", + ResourceType: mailtrap.ResourceTypeSandbox, + AccessLevel: mailtrap.PermissionLevelAdmin, + }, + { + ResourceID: "3000002", + ResourceType: mailtrap.ResourceTypeSandbox, + Destroy: true, + }, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Println("permissions updated") + } requestBody: content: application/json: @@ -682,6 +809,36 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.generalApi().permissions().getResources(accountId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + resources, _, err := client.Permissions.Resources(context.Background()) + if err != nil { + log.Fatal(err) + } + + // The response is a hierarchy: an account holds projects, which hold sandboxes. + for _, resource := range resources { + fmt.Printf("%s %d %s (%d child resource(s))\n", resource.Type, resource.ID, resource.Name, len(resource.Resources)) + } + } responses: '200': description: |- @@ -799,6 +956,35 @@ paths: long accountId = 1000001L; var tokens = client.generalApi().apiTokens().getAllApiTokens(accountId); System.out.println(tokens); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + tokens, _, err := client.APITokens.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, token := range tokens { + fmt.Printf("%d %s (...%s, expires %s)\n", token.ID, token.Name, token.Last4Digits, token.ExpiresAt) + } + } responses: '200': description: List of API tokens @@ -949,6 +1135,43 @@ paths: // The full token value is returned only once — store it securely. var token = client.generalApi().apiTokens().createApiToken(accountId, request); System.out.println(token); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + token, _, err := client.APITokens.Create(context.Background(), &mailtrap.CreateAPITokenRequest{ + Name: "CI deploy token", + Resources: []*mailtrap.APITokenPermission{ + { + ResourceType: mailtrap.ResourceTypeSandbox, + ResourceID: 3000001, + AccessLevel: mailtrap.AccessLevelAdmin, + }, + }, + }) + if err != nil { + log.Fatal(err) + } + + // The full token value is returned only here: store it securely. + fmt.Printf("created token %d: %s\n", token.ID, token.Token) + } requestBody: required: true content: @@ -1054,6 +1277,34 @@ paths: long apiTokenId = 123L; var token = client.generalApi().apiTokens().getApiToken(accountId, apiTokenId); System.out.println(token); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The full token value is not included; it is returned only on create and reset. + token, _, err := client.APITokens.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s (...%s), created by %s\n", token.Name, token.Last4Digits, token.CreatedBy) + } parameters: - $ref: '#/components/parameters/api_token_id' responses: @@ -1147,6 +1398,32 @@ paths: long accountId = 1000001L; long apiTokenId = 123L; client.generalApi().apiTokens().deleteApiToken(accountId, apiTokenId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.APITokens.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("API token deleted") + } parameters: - $ref: '#/components/parameters/api_token_id' responses: @@ -1259,6 +1536,36 @@ paths: // The new token value is returned only once — store it securely. var token = client.generalApi().apiTokens().resetApiToken(accountId, apiTokenId); System.out.println(token); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Reset expires the token and issues a replacement with the same permissions. + // Pass nil to send no body and apply the server default expiration. + token, _, err := client.APITokens.Reset(context.Background(), 1000001, nil) + if err != nil { + log.Fatal(err) + } + + // The new token value is returned only here: store it securely. + fmt.Printf("token %d reset: %s\n", token.ID, token.Token) + } parameters: - $ref: '#/components/parameters/api_token_id' requestBody: @@ -1369,6 +1676,38 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.generalApi().billing().getCurrentBillingCycleUsage(accountId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + usage, _, err := client.Billing.Usage(context.Background()) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("cycle %s to %s\n", usage.Billing.CycleStart, usage.Billing.CycleEnd) + // A product is nil when the account has no plan for it. + if usage.Sending != nil { + sent := usage.Sending.Usage.SentMessagesCount + fmt.Printf("sending (%s): %d of %d\n", usage.Sending.Plan.Name, sent.Current, sent.Limit) + } + } responses: '200': description: Returns an object with current billing cycle usage for Sandbox, Email Sending (Email API/SMTP), and Email Marketing if available. @@ -1560,6 +1899,36 @@ paths: long organizationId = 2000002L; var subAccounts = client.organizationsApi().subAccounts().getSubAccounts(organizationId); System.out.println(subAccounts); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN"), mailtrap.WithOrganizationID(2000001)) + if err != nil { + log.Fatal(err) + } + + // The sub-account endpoints target the organization set with WithOrganizationID. + subAccounts, _, err := client.SubAccounts.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, subAccount := range subAccounts { + fmt.Printf("%d %s\n", subAccount.ID, subAccount.Name) + } + } responses: '200': description: Returns the list of sub accounts belonging to the organization. @@ -1683,6 +2052,34 @@ paths: var subAccount = client.organizationsApi().subAccounts() .createSubAccount(organizationId, request); System.out.println(subAccount); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN"), mailtrap.WithOrganizationID(2000001)) + if err != nil { + log.Fatal(err) + } + + // The sub-account endpoints target the organization set with WithOrganizationID. + subAccount, _, err := client.SubAccounts.Create(context.Background(), "Acme Corp") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created sub-account %d (%s)\n", subAccount.ID, subAccount.Name) + } requestBody: required: true content: diff --git a/specs/contacts.openapi.yml b/specs/contacts.openapi.yml index 20e8d8a..da94eb9 100644 --- a/specs/contacts.openapi.yml +++ b/specs/contacts.openapi.yml @@ -217,6 +217,37 @@ paths: var contact = client.contactsApi().contacts() .createContact(accountId, request); System.out.println("Contact created: " + contact.getEmail()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + contact, _, err := client.Contacts.Create(context.Background(), &mailtrap.CreateContactRequest{ + Email: "john.smith@example.com", + Fields: map[string]any{"first_name": "John", "last_name": "Smith"}, + ListIDs: []int64{1, 2}, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created contact %s (%s)\n", contact.ID, contact.Email) + } requestBody: content: application/json: @@ -399,6 +430,34 @@ paths: System.out.println("Contact: " + contact.getEmail()); System.out.println("Status: " + contact.getStatus()); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The identifier is either the contact's UUID or its email address. + contact, _, err := client.Contacts.Get(context.Background(), "john.smith@example.com") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s (%s), lists %v\n", contact.Email, contact.Status, contact.ListIDs) + } responses: '200': description: OK. @@ -511,6 +570,39 @@ paths: var request = new UpdateContactRequest(new UpdateContact("new@example.com", Map.of("first_name", "John"), List.of(), List.of(), false)); var response = client.contactsApi().contacts().updateContact(accountId, "contact_id_or_email", request); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Update is an upsert and reports which action the API took. + result, _, err := client.Contacts.Update(context.Background(), "john.smith@example.com", &mailtrap.UpdateContactRequest{ + Email: "john.smith@example.com", + Fields: map[string]any{"first_name": "John"}, + ListIDsIncluded: []int64{3}, + ListIDsExcluded: []int64{1}, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s contact %s\n", result.Action, result.Contact.ID) + } summary: Update contact tags: - Contacts @@ -614,6 +706,32 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); client.contactsApi().contacts().deleteContact(accountId, "contact_id_or_email"); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.Contacts.Delete(context.Background(), "john.smith@example.com"); err != nil { + log.Fatal(err) + } + + fmt.Println("contact deleted") + } summary: Delete contact tags: - Contacts @@ -740,6 +858,36 @@ paths: Map params = Map.of("user_id", 101, "is_active", true); var request = new CreateContactEventRequest("UserLogin", params); var response = client.contactsApi().contactEvents().createContactEvent(accountId, "contact_id", request); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + event, _, err := client.ContactEvents.Create(context.Background(), "john.smith@example.com", &mailtrap.CreateContactEventRequest{ + Name: "purchase_completed", + Params: map[string]any{"order_id": "A-1024", "total": 49.99}, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("recorded %q for %s\n", event.Name, event.ContactEmail) + } requestBody: content: application/json: @@ -876,6 +1024,40 @@ paths: var idsFilter = ContactExportFilter.listIds(ContactExportFilterOperator.EQUAL, 1L, 2L); var request = new CreateContactsExportRequest(List.of(idsFilter)); var response = client.contactsApi().contactExports().createContactExport(accountId, request); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Pass nil to export every contact. + export, _, err := client.ContactExports.Create(context.Background(), []*mailtrap.ContactExportFilter{ + { + Name: mailtrap.ContactExportFilterListID, + Operator: mailtrap.ContactExportOperatorEqual, + Value: []int64{1, 2}, + }, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("export %d is %s\n", export.ID, export.Status) + } requestBody: content: application/json: @@ -988,6 +1170,38 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.contactsApi().exports().get(accountId, exportId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Poll until the status is "finished", at which point URL is set. + export, _, err := client.ContactExports.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + if export.Status == mailtrap.ContactExportFinished { + fmt.Printf("download: %s\n", *export.URL) + } else { + fmt.Printf("export %d is %s\n", export.ID, export.Status) + } + } responses: '200': description: OK. @@ -1125,6 +1339,44 @@ paths: var contact = new Contact("user1@example.com", Map.of("first_name", "John"), Collections.emptyList(), Collections.emptyList()); var importContactsRequest = new ImportContactsRequest(List.of(contact)); client.contactsApi().contactImports().importContacts(accountId, importContactsRequest); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + result, _, err := client.ContactImports.Create(context.Background(), []*mailtrap.ImportContact{ + { + Email: "john.smith@example.com", + Fields: map[string]any{"first_name": "John"}, + ListIDsIncluded: []int64{1}, + }, + { + Email: "jane.doe@example.com", + ListIDsIncluded: []int64{1}, + ListIDsExcluded: []int64{2}, + }, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("import %d is %s\n", result.ID, result.Status) + } requestBody: content: application/json: @@ -1236,6 +1488,34 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.contactsApi().contactImports().getContactImport(accountId, importId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The counts are populated once the status is "finished". + result, _, err := client.ContactImports.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s: %d created, %d updated, %d over limit\n", result.Status, result.CreatedContactsCount, result.UpdatedContactsCount, result.ContactsOverLimitCount) + } responses: '200': description: OK. @@ -1348,6 +1628,37 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.contactsApi().contactLists().findAll(accountId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + lists, _, err := client.ContactLists.List(context.Background(), &mailtrap.ContactListListOptions{ + Search: "newsletter", + }) + if err != nil { + log.Fatal(err) + } + + for _, list := range lists { + fmt.Printf("%d %s\n", list.ID, list.Name) + } + } responses: '200': description: OK. @@ -1442,6 +1753,33 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.contactsApi().contactLists().createContactList(accountId, new CreateUpdateContactListRequest("Customers")); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + list, _, err := client.ContactLists.Create(context.Background(), "Monthly newsletter") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created list %d (%s)\n", list.ID, list.Name) + } requestBody: content: application/json: @@ -1539,6 +1877,33 @@ paths: long listId = 1L; var response = client.contactsApi().contactLists().getContactList(accountId, listId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + list, _, err := client.ContactLists.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%d %s\n", list.ID, list.Name) + } responses: '200': description: Returns attributes of the contact list. @@ -1629,6 +1994,33 @@ paths: long listId = 1L; var response = client.contactsApi().contactLists().updateContactList(accountId, listId, new CreateUpdateContactListRequest("Former Customers")); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + list, _, err := client.ContactLists.Update(context.Background(), 1000001, "Weekly newsletter") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("renamed list %d to %s\n", list.ID, list.Name) + } requestBody: content: application/json: @@ -1726,6 +2118,32 @@ paths: long listId = 1L; client.contactsApi().contactLists().deleteContactList(accountId, listId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.ContactLists.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("contact list deleted") + } responses: '204': description: Contact List successfully deleted @@ -1808,6 +2226,35 @@ paths: var client = MailtrapClientFactory.createMailtrapClient(config); var response = client.contactsApi().contactFields().getAllContactFields(accountId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + fields, _, err := client.ContactFields.List(context.Background()) + if err != nil { + log.Fatal(err) + } + + for _, field := range fields { + fmt.Printf("%d %s (%s, merge tag %s)\n", field.ID, field.Name, field.DataType, field.MergeTag) + } + } responses: '200': description: OK. @@ -1914,6 +2361,37 @@ paths: var request = new CreateContactFieldRequest("Company", ContactFieldDataType.TEXT, "company"); var response = client.contactsApi().contactFields().createContactField(accountId, request); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + field, _, err := client.ContactFields.Create(context.Background(), &mailtrap.CreateContactFieldRequest{ + Name: "First name", + DataType: mailtrap.ContactFieldTypeText, + MergeTag: "first_name", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created field %d (%s)\n", field.ID, field.MergeTag) + } requestBody: content: application/json: @@ -2043,6 +2521,33 @@ paths: long fieldId = 2L; var response = client.contactsApi().contactFields().getContactField(accountId, fieldId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + field, _, err := client.ContactFields.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s (%s), merge tag %s\n", field.Name, field.DataType, field.MergeTag) + } responses: '200': description: Returns attributes of the contact field. @@ -2136,6 +2641,37 @@ paths: var request = new UpdateContactFieldRequest("Updated Name", "updated_name"); var response = client.contactsApi().contactFields().updateContactField(accountId, fieldId, request); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The data type is immutable: only the name and merge tag can change. + field, _, err := client.ContactFields.Update(context.Background(), 1000001, &mailtrap.UpdateContactFieldRequest{ + Name: "Given name", + MergeTag: "given_name", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("updated field %d: %s (%s)\n", field.ID, field.Name, field.MergeTag) + } requestBody: content: application/json: @@ -2254,6 +2790,32 @@ paths: long fieldId = 2L; client.contactsApi().contactFields().deleteContactField(accountId, fieldId); + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + if _, err := client.ContactFields.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("contact field deleted") + } responses: '204': description: Contact Field successfully deleted From 641901f7ac8e666d35066fa91cdac6ee037d9f06 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 1 Sep 2026 11:51:08 +0200 Subject: [PATCH 5/9] Add OpenTofu code samples to the API specs 30 samples across sandbox, contacts, email-sending and account-management, completing the provider's coverage at 34 of the 130 documented operations. A resource block is not an API call, so every sample opens with a comment naming the lifecycle command that fires the endpoint, and each sample is self-contained down to the required_providers block. Where the provider has a data source (project, sandbox, domain, account) the read samples use it; the rest pair the resource block with a tofu import line. Attribute names come from the provider schema rather than its published examples, so the resource is mailtrap_domain and every email_sending webhook sets sending_stream. The samples also carry the provider's real footguns as comments: the API token has no update path, an imported token or webhook has no secret, and a template body cannot be cleared. Co-Authored-By: Claude Opus 5 (1M context) --- specs/account-management.openapi.yml | 104 +++++++++++++ specs/contacts.openapi.yml | 158 ++++++++++++++++++++ specs/email-sending.openapi.yml | 189 ++++++++++++++++++++++++ specs/sandbox.openapi.yml | 209 +++++++++++++++++++++++++++ 4 files changed, 660 insertions(+) diff --git a/specs/account-management.openapi.yml b/specs/account-management.openapi.yml index 891fb3b..3c87552 100644 --- a/specs/account-management.openapi.yml +++ b/specs/account-management.openapi.yml @@ -164,6 +164,29 @@ paths: fmt.Printf("%d %s (access levels %v)\n", account.ID, account.Name, account.AccessLevels) } } + - lang: hcl + label: OpenTofu + source: | + # looking an account up lists them: GET /api/accounts + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # With no filters the token must map to exactly one account. + data "mailtrap_account" "current" { + name = "Acme Corp" + } + + output "account_id" { + value = data.mailtrap_account.current.id + } responses: '200': description: 'Returns the list of accounts to which the API token has access. **access_levels** can return 1000 (account owner), 100 (admin), 10 (viewer).' @@ -1172,6 +1195,38 @@ paths: // The full token value is returned only here: store it securely. fmt.Printf("created token %d: %s\n", token.ID, token.Token) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the token: POST /api/api_tokens + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_api_token" "ci" { + name = "CI deploy token" + + resources = [ + { + resource_type = "sandbox" + resource_id = 3000001 + access_level = 100 + }, + ] + } + + # token is returned only on create, and is unavailable after an import. + output "api_token" { + value = mailtrap_api_token.ci.token + sensitive = true + } requestBody: required: true content: @@ -1305,6 +1360,36 @@ paths: fmt.Printf("%s (...%s), created by %s\n", token.Name, token.Last4Digits, token.CreatedBy) } + - lang: hcl + label: OpenTofu + source: | + # tofu plan refreshes the resource, and tofu import mailtrap_api_token.ci 1000001 + # adopts an existing one: GET /api/api_tokens/{id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # The token has no update path: every attribute forces replacement. Permission + # drift made outside OpenTofu is not detected, and token is unavailable after an + # import. + resource "mailtrap_api_token" "ci" { + name = "CI deploy token" + + resources = [ + { + resource_type = "sandbox" + resource_id = 3000001 + access_level = 100 + }, + ] + } parameters: - $ref: '#/components/parameters/api_token_id' responses: @@ -1424,6 +1509,25 @@ paths: fmt.Println("API token deleted") } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/api_tokens/{id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_api_token" "ci" { + name = "CI deploy token" + } parameters: - $ref: '#/components/parameters/api_token_id' responses: diff --git a/specs/contacts.openapi.yml b/specs/contacts.openapi.yml index da94eb9..147d4bc 100644 --- a/specs/contacts.openapi.yml +++ b/specs/contacts.openapi.yml @@ -1780,6 +1780,24 @@ paths: fmt.Printf("created list %d (%s)\n", list.ID, list.Name) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the list: POST /api/contacts/lists + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_contact_list" "newsletter" { + name = "Monthly newsletter" + } requestBody: content: application/json: @@ -1904,6 +1922,25 @@ paths: fmt.Printf("%d %s\n", list.ID, list.Name) } + - lang: hcl + label: OpenTofu + source: | + # tofu plan refreshes the resource, and tofu import mailtrap_contact_list.newsletter 1000001 + # adopts an existing one: GET /api/contacts/lists/{list_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_contact_list" "newsletter" { + name = "Monthly newsletter" + } responses: '200': description: Returns attributes of the contact list. @@ -2021,6 +2058,24 @@ paths: fmt.Printf("renamed list %d to %s\n", list.ID, list.Name) } + - lang: hcl + label: OpenTofu + source: | + # changing the name and running tofu apply: PATCH /api/contacts/lists/{list_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_contact_list" "newsletter" { + name = "Weekly newsletter" + } requestBody: content: application/json: @@ -2144,6 +2199,25 @@ paths: fmt.Println("contact list deleted") } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/contacts/lists/{list_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_contact_list" "newsletter" { + name = "Monthly newsletter" + } responses: '204': description: Contact List successfully deleted @@ -2392,6 +2466,26 @@ paths: fmt.Printf("created field %d (%s)\n", field.ID, field.MergeTag) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the field: POST /api/contacts/fields + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_contact_field" "first_name" { + name = "First name" + data_type = "text" + merge_tag = "first_name" + } requestBody: content: application/json: @@ -2548,6 +2642,27 @@ paths: fmt.Printf("%s (%s), merge tag %s\n", field.Name, field.DataType, field.MergeTag) } + - lang: hcl + label: OpenTofu + source: | + # tofu plan refreshes the resource, and tofu import mailtrap_contact_field.first_name 1000001 + # adopts an existing one: GET /api/contacts/fields/{field_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_contact_field" "first_name" { + name = "First name" + data_type = "text" + merge_tag = "first_name" + } responses: '200': description: Returns attributes of the contact field. @@ -2672,6 +2787,28 @@ paths: fmt.Printf("updated field %d: %s (%s)\n", field.ID, field.Name, field.MergeTag) } + - lang: hcl + label: OpenTofu + source: | + # changing the name or merge tag and running tofu apply: + # PATCH /api/contacts/fields/{field_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # data_type is immutable: changing it forces the field to be replaced. + resource "mailtrap_contact_field" "first_name" { + name = "Given name" + data_type = "text" + merge_tag = "given_name" + } requestBody: content: application/json: @@ -2816,6 +2953,27 @@ paths: fmt.Println("contact field deleted") } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/contacts/fields/{field_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_contact_field" "first_name" { + name = "First name" + data_type = "text" + merge_tag = "first_name" + } responses: '204': description: Contact Field successfully deleted diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index 5ebd5b6..ffebb2f 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -216,6 +216,28 @@ paths: fmt.Printf(" %s %s %q -> %q\n", record.Key, record.Type, record.Name, record.Value) } } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the domain: POST /api/domains + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # Create only accepts domain_name. The tracking flags below make the same apply + # follow up with a PATCH. + resource "mailtrap_domain" "example" { + domain_name = "mail.example.com" + open_tracking_enabled = true + click_tracking_enabled = true + } requestBody: required: true content: @@ -522,6 +544,29 @@ paths: fmt.Printf("%s: DNS verified %t, open tracking %t\n", domain.DomainName, domain.DNSVerified, domain.OpenTrackingEnabled) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply reads the domain: GET /api/domains/{domain_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # The domain data source can only be looked up by id: there is no name lookup. + data "mailtrap_domain" "example" { + id = 1000001 + } + + output "dns_verified" { + value = data.mailtrap_domain.example.dns_verified + } parameters: - $ref: '#/components/parameters/domain_id' responses: @@ -660,6 +705,25 @@ paths: fmt.Println("domain deleted") } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/domains/{domain_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_domain" "example" { + domain_name = "mail.example.com" + } parameters: - $ref: '#/components/parameters/domain_id' responses: @@ -744,6 +808,28 @@ paths: fmt.Printf("%s: open tracking %t, click tracking %t\n", domain.DomainName, domain.OpenTrackingEnabled, domain.ClickTrackingEnabled) } + - lang: hcl + label: OpenTofu + source: | + # changing a tracked attribute and running tofu apply: + # PATCH /api/domains/{domain_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_domain" "example" { + domain_name = "mail.example.com" + open_tracking_enabled = true + click_tracking_enabled = false + auto_unsubscribe_link_enabled = true + } parameters: - $ref: '#/components/parameters/domain_id' requestBody: @@ -3081,6 +3167,37 @@ paths: // The signing secret is returned only here: store it to verify payloads. fmt.Printf("created webhook %d, signing secret %s\n", webhook.ID, webhook.SigningSecret) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the webhook: POST /api/webhooks + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # The API requires sending_stream for email_sending webhooks. + resource "mailtrap_webhook" "delivery" { + url = "https://example.com/mailtrap/webhook" + webhook_type = "email_sending" + sending_stream = "transactional" + domain_id = 1000001 + payload_format = "json" + event_types = ["delivery", "bounce", "open"] + active = true + } + + # signing_secret is returned only on create, and is unavailable after an import. + output "signing_secret" { + value = mailtrap_webhook.delivery.signing_secret + sensitive = true + } requestBody: required: true content: @@ -3446,6 +3563,29 @@ paths: fmt.Printf("%s (%s): %v\n", webhook.URL, webhook.PayloadFormat, webhook.EventTypes) } + - lang: hcl + label: OpenTofu + source: | + # tofu plan refreshes the resource, and tofu import mailtrap_webhook.delivery 1000001 + # adopts an existing one: GET /api/webhooks/{webhook_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # An imported webhook has no signing_secret: the API returns it only on create. + resource "mailtrap_webhook" "delivery" { + url = "https://example.com/mailtrap/webhook" + webhook_type = "email_sending" + sending_stream = "transactional" + domain_id = 1000001 + } parameters: - $ref: '#/components/parameters/webhook_id' responses: @@ -3609,6 +3749,33 @@ paths: fmt.Printf("updated webhook %d: %s (active: %t)\n", webhook.ID, webhook.URL, webhook.Active) } + - lang: hcl + label: OpenTofu + source: | + # changing a mutable attribute and running tofu apply: + # PATCH /api/webhooks/{webhook_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + # Only url, active, payload_format and event_types are mutable; changing + # webhook_type, sending_stream or domain_id replaces the webhook. + resource "mailtrap_webhook" "delivery" { + url = "https://example.com/mailtrap/webhook/v2" + webhook_type = "email_sending" + sending_stream = "transactional" + domain_id = 1000001 + payload_format = "jsonlines" + event_types = ["delivery", "spam_complaint"] + active = false + } parameters: - $ref: '#/components/parameters/webhook_id' requestBody: @@ -3804,6 +3971,28 @@ paths: fmt.Printf("deleted webhook %d (%s)\n", webhook.ID, webhook.URL) } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/webhooks/{webhook_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_webhook" "delivery" { + url = "https://example.com/mailtrap/webhook" + webhook_type = "email_sending" + sending_stream = "transactional" + domain_id = 1000001 + } parameters: - $ref: '#/components/parameters/webhook_id' responses: diff --git a/specs/sandbox.openapi.yml b/specs/sandbox.openapi.yml index 0536627..8f5130f 100644 --- a/specs/sandbox.openapi.yml +++ b/specs/sandbox.openapi.yml @@ -190,6 +190,28 @@ paths: fmt.Printf("%d %s (%d message(s), %d unread)\n", sandbox.ID, sandbox.Name, sandbox.EmailsCount, sandbox.EmailsUnreadCount) } } + - lang: hcl + label: OpenTofu + source: | + # looking a sandbox up by name lists them: GET /api/sandboxes + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + data "mailtrap_sandbox" "staging" { + name = "Staging inbox" + } + + output "sandbox_id" { + value = data.mailtrap_sandbox.staging.id + } operationId: getSandboxes '/api/projects': post: @@ -359,6 +381,24 @@ paths: fmt.Printf("created project %d (%s)\n", project.ID, project.Name) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the project: POST /api/projects + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_project" "testing" { + name = "My project" + } get: summary: Get a list of projects description: List projects and their sandboxes to which the API token has access. @@ -504,6 +544,28 @@ paths: fmt.Printf("%d %s (%d sandbox(es))\n", project.ID, project.Name, len(project.Sandboxes)) } } + - lang: hcl + label: OpenTofu + source: | + # looking a project up by name lists them: GET /api/projects + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + data "mailtrap_project" "testing" { + name = "My project" + } + + output "project_id" { + value = data.mailtrap_project.testing.id + } '/api/projects/{project_id}': get: summary: Get project by ID @@ -651,6 +713,28 @@ paths: fmt.Printf("%d %s\n", project.ID, project.Name) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply reads the project: GET /api/projects/{project_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + data "mailtrap_project" "testing" { + id = 1000001 + } + + output "project_name" { + value = data.mailtrap_project.testing.name + } patch: summary: Update project description: Update project name. The project name is min 2 characters and max 100 characters long. @@ -810,6 +894,24 @@ paths: fmt.Printf("renamed project %d to %s\n", project.ID, project.Name) } + - lang: hcl + label: OpenTofu + source: | + # changing the name and running tofu apply: PATCH /api/projects/{project_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_project" "testing" { + name = "Renamed project" + } requestBody: content: application/json: @@ -968,6 +1070,25 @@ paths: fmt.Println("project deleted") } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/projects/{project_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_project" "testing" { + name = "My project" + } responses: '200': description: Returns id of the deleted project. @@ -1169,6 +1290,31 @@ paths: fmt.Printf("created sandbox %d (%s)\n", sandbox.ID, sandbox.Name) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply creates the sandbox: POST /api/projects/{project_id}/sandboxes + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_project" "testing" { + name = "My project" + } + + # Create only accepts name and project_id. Setting email_username makes the same + # apply follow up with a PATCH. + resource "mailtrap_sandbox" "staging" { + name = "Staging inbox" + project_id = mailtrap_project.testing.id + } parameters: - $ref: '#/components/parameters/project_id' '/api/sandboxes/{sandbox_id}': @@ -1311,6 +1457,28 @@ paths: fmt.Printf("%s: %s@%s, SMTP %v\n", sandbox.Name, sandbox.Username, sandbox.Domain, sandbox.SMTPPorts) } + - lang: hcl + label: OpenTofu + source: | + # tofu apply reads the sandbox: GET /api/sandboxes/{sandbox_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + data "mailtrap_sandbox" "staging" { + id = 3000001 + } + + output "smtp_host" { + value = data.mailtrap_sandbox.staging.domain + } operationId: getSandboxAttributes delete: summary: Delete a sandbox @@ -1450,6 +1618,26 @@ paths: fmt.Printf("deleted sandbox %d (%s)\n", sandbox.ID, sandbox.Name) } + - lang: hcl + label: OpenTofu + source: | + # tofu destroy, or removing this block and running tofu apply: + # DELETE /api/sandboxes/{sandbox_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_sandbox" "staging" { + name = "Staging inbox" + project_id = 1000001 + } operationId: deleteSandbox patch: summary: Update a sandbox @@ -1638,6 +1826,27 @@ paths: fmt.Printf("updated sandbox %d: %s\n", sandbox.ID, sandbox.Name) } + - lang: hcl + label: OpenTofu + source: | + # changing a tracked attribute and running tofu apply: + # PATCH /api/sandboxes/{sandbox_id} + terraform { + required_providers { + mailtrap = { + source = "mailtrap/mailtrap" + } + } + } + + # The token is read from the MAILTRAP_API_TOKEN environment variable. + provider "mailtrap" {} + + resource "mailtrap_sandbox" "staging" { + name = "Renamed inbox" + project_id = 1000001 + email_username = "staging" + } operationId: updateSandbox parameters: - $ref: '#/components/parameters/sandbox_id' From 86674116732e451c04508e68454a51c20d20df68 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Tue, 1 Sep 2026 11:52:21 +0200 Subject: [PATCH 6/9] Add Go code samples to the email campaigns endpoints 11 samples, one per campaign operation, covering the draft/schedule/start lifecycle and the stats endpoint. The update sample shows the pointer-to-slice fields: nil leaves the contact lists and segments unchanged, while a pointer to an empty slice clears them. The list sample also shows the EmailCampaigns.All iterator. Deliberately the last commit on this branch: PR #54 is adding the six other languages to these same x-codeSamples lists, so this commit is the one to rebase or drop once #54 lands. Co-Authored-By: Claude Opus 5 (1M context) --- specs/email-campaigns.openapi.yml | 352 ++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) diff --git a/specs/email-campaigns.openapi.yml b/specs/email-campaigns.openapi.yml index 0f26dc5..01b4f23 100644 --- a/specs/email-campaigns.openapi.yml +++ b/specs/email-campaigns.openapi.yml @@ -58,6 +58,48 @@ paths: source: | curl -X GET "https://mailtrap.io/api/email_campaigns?per_page=50&token=1" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + page, _, err := client.EmailCampaigns.List(context.Background(), &mailtrap.EmailCampaignListOptions{ + PerPage: 50, + Search: "newsletter", + }) + if err != nil { + log.Fatal(err) + } + + for _, campaign := range page.Data { + fmt.Printf("%d %s (%s)\n", campaign.ID, campaign.Name, campaign.CurrentState) + } + + // To walk every page instead of one, use the iterator, which follows + // pagination.next_token for you: + // + // for campaign, err := range client.EmailCampaigns.All(context.Background(), nil) { + // if err != nil { + // log.Fatal(err) + // } + // fmt.Println(campaign.Name) + // } + } post: operationId: createEmailCampaign summary: Create an email campaign @@ -107,6 +149,53 @@ paths: }, "template_attributes": { "subject": "Spring is here — 30% off" } }' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // A campaign is always created as a draft; scheduling and starting it are + // separate actions. + campaign, _, err := client.EmailCampaigns.Create(context.Background(), &mailtrap.CreateEmailCampaignRequest{ + Name: "March newsletter", + DomainID: 1000001, + FromDisplayName: "Acme Corp", + FromLocalPart: "news", + ReplyTo: &mailtrap.EmailCampaignReplyTo{ + DisplayName: "Acme Support", + LocalPart: "support", + Domain: "mail.example.com", + }, + TemplateAttributes: &mailtrap.EmailCampaignTemplateAttributes{ + Subject: "Hi {{first_name}}, here is March", + BodyHTML: "

Hello {{first_name}}

Unsubscribe", + MergeTags: []string{"first_name"}, + }, + DeliveryMode: mailtrap.EmailCampaignDeliveryModeGradual, + DeliveryOptions: &mailtrap.EmailCampaignDeliveryOptions{EmailsPerHour: mailtrap.Ptr(5000)}, + ContactListIDs: []int64{1, 2}, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("created campaign %d (%s)\n", campaign.ID, campaign.CurrentState) + } "/api/email_campaigns/{email_campaign_id}": get: operationId: getEmailCampaign @@ -133,6 +222,33 @@ paths: source: | curl -X GET "https://mailtrap.io/api/email_campaigns/4567" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + campaign, _, err := client.EmailCampaigns.Get(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s: %s, subject %q\n", campaign.Name, campaign.CurrentState, campaign.Template.Subject) + } patch: operationId: updateEmailCampaign summary: Update an email campaign @@ -186,6 +302,43 @@ paths: "body_html": "

Hi {{first_name}}!

Unsubscribe

" } }' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Only a draft campaign can be updated, and only the fields you set change. + // The list and segment fields are pointers: nil leaves them unchanged, while a + // pointer to an empty slice clears them. + campaign, _, err := client.EmailCampaigns.Update(context.Background(), 1000001, &mailtrap.UpdateEmailCampaignRequest{ + Name: "March newsletter (final)", + TemplateAttributes: &mailtrap.EmailCampaignTemplateAttributes{ + Subject: "Hi {{first_name}}, March is here", + }, + ContactListIDs: mailtrap.Ptr([]int64{3}), + ContactSegmentIDs: mailtrap.Ptr([]int64{}), + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("updated campaign %d: %s\n", campaign.ID, campaign.Name) + } delete: operationId: deleteEmailCampaign summary: Delete an email campaign @@ -219,6 +372,33 @@ paths: source: | curl -X DELETE "https://mailtrap.io/api/email_campaigns/4567" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Only a campaign in the draft state can be deleted. + if _, err := client.EmailCampaigns.Delete(context.Background(), 1000001); err != nil { + log.Fatal(err) + } + + fmt.Println("campaign deleted") + } "/api/email_campaigns/{email_campaign_id}/start": post: operationId: startEmailCampaign @@ -252,6 +432,33 @@ paths: source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/start" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + campaign, _, err := client.EmailCampaigns.Start(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) + } "/api/email_campaigns/{email_campaign_id}/schedule": post: operationId: scheduleEmailCampaign @@ -294,6 +501,34 @@ paths: -H 'Api-Token: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "datetime": "2026-06-01T09:00:00.000Z" }' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // The datetime is ISO 8601, must be in the future and no more than a month ahead. + campaign, _, err := client.EmailCampaigns.Schedule(context.Background(), 1000001, "2026-10-01T09:00:00Z") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("campaign %d is %s for %s\n", campaign.ID, campaign.CurrentState, campaign.CurrentStateMetadata.ScheduledAt) + } "/api/email_campaigns/{email_campaign_id}/cancel": post: operationId: cancelEmailCampaign @@ -325,6 +560,34 @@ paths: source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/cancel" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Cancel returns a scheduled campaign to the draft state. + campaign, _, err := client.EmailCampaigns.Cancel(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) + } "/api/email_campaigns/{email_campaign_id}/terminate": post: operationId: terminateEmailCampaign @@ -356,6 +619,34 @@ paths: source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/terminate" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Terminate aborts a campaign that is started, queued or paused. + campaign, _, err := client.EmailCampaigns.Terminate(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) + } "/api/email_campaigns/{email_campaign_id}/reset": post: operationId: resetEmailCampaign @@ -386,6 +677,34 @@ paths: source: | curl -X POST "https://mailtrap.io/api/email_campaigns/4567/reset" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Reset returns a scheduled campaign to the draft state. + campaign, _, err := client.EmailCampaigns.Reset(context.Background(), 1000001) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("campaign %d is %s\n", campaign.ID, campaign.CurrentState) + } "/api/email_campaigns/{email_campaign_id}/stats": get: operationId: getEmailCampaignStats @@ -424,6 +743,39 @@ paths: source: | curl -X GET "https://mailtrap.io/api/email_campaigns/4567/stats?start_date=2026-05-01&end_date=2026-05-31" \ -H 'Api-Token: YOUR_API_KEY' + - lang: go + label: Go + source: | + package main + + import ( + "context" + "fmt" + "log" + "os" + + "github.com/mailtrap/mailtrap-go" + ) + + func main() { + client, err := mailtrap.NewClient(os.Getenv("MAILTRAP_API_TOKEN")) + if err != nil { + log.Fatal(err) + } + + // Pass nil for the default window, which is the period since the campaign was + // last started. + stats, _, err := client.EmailCampaigns.Stats(context.Background(), 1000001, &mailtrap.EmailCampaignStatsOptions{ + StartDate: "2026-03-01", + EndDate: "2026-03-31", + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("delivered %d (%.2f), opened %d (%.2f), clicked %d (%.2f)\n", + stats.DeliveryCount, stats.DeliveryRate, stats.OpenCount, stats.OpenRate, stats.ClickCount, stats.ClickRate) + } components: securitySchemes: HeaderAuth: From dcebae33cc8d02b1b7a5a8a8a7da2858a6bdf290 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Wed, 2 Sep 2026 09:18:59 +0200 Subject: [PATCH 7/9] Label the HCL code samples Terraform instead of OpenTofu The provider is `terraform-provider-mailtrap` and ships to both registries; Terraform is the name readers of the docs dropdown will recognise. - `label: OpenTofu` -> `label: Terraform` on all 34 hcl samples (`lang: hcl` is unchanged - Prism still has no `terraform` component). - Lifecycle comments now name the terraform CLI: `terraform apply/plan/ import/destroy`. The commands are interchangeable, so the configs are unaffected. - CLAUDE.md conventions updated to match, so future samples are added as Terraform. Re-verified: Spectral clean, and all 34 samples extracted back out of the committed YAML are `terraform fmt -check` clean. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 18 +++++++------- specs/account-management.openapi.yml | 16 ++++++------- specs/contacts.openapi.yml | 32 ++++++++++++------------- specs/email-sending.openapi.yml | 32 ++++++++++++------------- specs/sandbox.openapi.yml | 36 ++++++++++++++-------------- specs/templates.openapi.yml | 16 ++++++------- 6 files changed, 75 insertions(+), 75 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f342ed8..a4fbe4d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -149,7 +149,7 @@ Include code samples in this order: 6. .NET (C#) 7. Java 8. Go (`lang: go`, `label: Go`) -9. OpenTofu (`lang: hcl`, `label: OpenTofu`) - HCL, not an API call; see below +9. Terraform (`lang: hcl`, `label: Terraform`) - HCL, not an API call; see below ### Code Sample Format @@ -177,8 +177,8 @@ x-codeSamples: - **Use official Mailtrap SDKs** for language-specific examples - **If SDK doesn't support a method**, either let GitBook generate the example or add a comment noting SDK limitations -- **For Go and OpenTofu, omit the entry entirely** when the tool cannot express the operation. A tab that - says "unsupported" is worse than an absent tab, so `inbound.openapi.yml` gets no Go or OpenTofu samples +- **For Go and Terraform, omit the entry entirely** when the tool cannot express the operation. A tab that + says "unsupported" is worse than an absent tab, so `inbound.openapi.yml` gets no Go or Terraform samples at all, and the tracking opt-out operations keep their cURL-only sample. - **Use environment variables** for API keys (e.g., `process.env.MAILTRAP_API_KEY`) - **Use Context7 MCP** to query SDK capabilities when unsure @@ -199,19 +199,19 @@ x-codeSamples: *indentation* must be spaces), so a `source: |` block indented with 12 spaces followed by tabs round-trips correctly. -#### OpenTofu samples +#### Terraform samples -- Use `lang: hcl` with `label: OpenTofu`. GitBook highlights with Prism, which has an `hcl` component and +- Use `lang: hcl` with `label: Terraform`. GitBook highlights with Prism, which has an `hcl` component and no `terraform` one; `lang` resolves through Prism identifiers rather than GitBook's documented linguist list, as the existing `csharp` and `shell` samples show. The dropdown tab is titled by `label`, so it - reads "OpenTofu" regardless. + reads "Terraform" regardless. - An HCL block is not an API call: a `resource` block declares desired state and the endpoint fires as a side effect of a lifecycle command. Every sample therefore opens with a comment naming both the command - and the operation, e.g. `# tofu apply creates the domain: POST /api/domains`. + and the operation, e.g. `# terraform apply creates the domain: POST /api/domains`. - Each sample is self-contained, including the `terraform { required_providers { ... } }` and `provider "mailtrap" {}` blocks, because the sample tab is where a reader learns the source string. - Take attribute names from the provider schema, not from its published examples. Verify with - `tofu fmt -check` plus `tofu validate` against a locally built provider binary under `dev_overrides`. + `terraform fmt -check` plus `terraform validate` against a locally built provider binary under `dev_overrides`. ### SDK Repositories @@ -223,7 +223,7 @@ Reference SDK repos for accurate code examples: - .NET: Future reference - Java: Future reference - Go: `mailtrap/mailtrap-go` -- OpenTofu/Terraform provider: `mailtrap/terraform-provider-mailtrap` +- Terraform provider: `mailtrap/terraform-provider-mailtrap` ## OpenAPI Extensions diff --git a/specs/account-management.openapi.yml b/specs/account-management.openapi.yml index 3c87552..c3cfefd 100644 --- a/specs/account-management.openapi.yml +++ b/specs/account-management.openapi.yml @@ -165,7 +165,7 @@ paths: } } - lang: hcl - label: OpenTofu + label: Terraform source: | # looking an account up lists them: GET /api/accounts terraform { @@ -1196,9 +1196,9 @@ paths: fmt.Printf("created token %d: %s\n", token.ID, token.Token) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the token: POST /api/api_tokens + # terraform apply creates the token: POST /api/api_tokens terraform { required_providers { mailtrap = { @@ -1361,9 +1361,9 @@ paths: fmt.Printf("%s (...%s), created by %s\n", token.Name, token.Last4Digits, token.CreatedBy) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu plan refreshes the resource, and tofu import mailtrap_api_token.ci 1000001 + # terraform plan refreshes the resource, and terraform import mailtrap_api_token.ci 1000001 # adopts an existing one: GET /api/api_tokens/{id} terraform { required_providers { @@ -1377,7 +1377,7 @@ paths: provider "mailtrap" {} # The token has no update path: every attribute forces replacement. Permission - # drift made outside OpenTofu is not detected, and token is unavailable after an + # drift made outside Terraform is not detected, and token is unavailable after an # import. resource "mailtrap_api_token" "ci" { name = "CI deploy token" @@ -1510,9 +1510,9 @@ paths: fmt.Println("API token deleted") } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/api_tokens/{id} terraform { required_providers { diff --git a/specs/contacts.openapi.yml b/specs/contacts.openapi.yml index 147d4bc..65c49de 100644 --- a/specs/contacts.openapi.yml +++ b/specs/contacts.openapi.yml @@ -1781,9 +1781,9 @@ paths: fmt.Printf("created list %d (%s)\n", list.ID, list.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the list: POST /api/contacts/lists + # terraform apply creates the list: POST /api/contacts/lists terraform { required_providers { mailtrap = { @@ -1923,9 +1923,9 @@ paths: fmt.Printf("%d %s\n", list.ID, list.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu plan refreshes the resource, and tofu import mailtrap_contact_list.newsletter 1000001 + # terraform plan refreshes the resource, and terraform import mailtrap_contact_list.newsletter 1000001 # adopts an existing one: GET /api/contacts/lists/{list_id} terraform { required_providers { @@ -2059,9 +2059,9 @@ paths: fmt.Printf("renamed list %d to %s\n", list.ID, list.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # changing the name and running tofu apply: PATCH /api/contacts/lists/{list_id} + # changing the name and running terraform apply: PATCH /api/contacts/lists/{list_id} terraform { required_providers { mailtrap = { @@ -2200,9 +2200,9 @@ paths: fmt.Println("contact list deleted") } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/contacts/lists/{list_id} terraform { required_providers { @@ -2467,9 +2467,9 @@ paths: fmt.Printf("created field %d (%s)\n", field.ID, field.MergeTag) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the field: POST /api/contacts/fields + # terraform apply creates the field: POST /api/contacts/fields terraform { required_providers { mailtrap = { @@ -2643,9 +2643,9 @@ paths: fmt.Printf("%s (%s), merge tag %s\n", field.Name, field.DataType, field.MergeTag) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu plan refreshes the resource, and tofu import mailtrap_contact_field.first_name 1000001 + # terraform plan refreshes the resource, and terraform import mailtrap_contact_field.first_name 1000001 # adopts an existing one: GET /api/contacts/fields/{field_id} terraform { required_providers { @@ -2788,9 +2788,9 @@ paths: fmt.Printf("updated field %d: %s (%s)\n", field.ID, field.Name, field.MergeTag) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # changing the name or merge tag and running tofu apply: + # changing the name or merge tag and running terraform apply: # PATCH /api/contacts/fields/{field_id} terraform { required_providers { @@ -2954,9 +2954,9 @@ paths: fmt.Println("contact field deleted") } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/contacts/fields/{field_id} terraform { required_providers { diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index ffebb2f..50f65c0 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -217,9 +217,9 @@ paths: } } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the domain: POST /api/domains + # terraform apply creates the domain: POST /api/domains terraform { required_providers { mailtrap = { @@ -545,9 +545,9 @@ paths: fmt.Printf("%s: DNS verified %t, open tracking %t\n", domain.DomainName, domain.DNSVerified, domain.OpenTrackingEnabled) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply reads the domain: GET /api/domains/{domain_id} + # terraform apply reads the domain: GET /api/domains/{domain_id} terraform { required_providers { mailtrap = { @@ -706,9 +706,9 @@ paths: fmt.Println("domain deleted") } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/domains/{domain_id} terraform { required_providers { @@ -809,9 +809,9 @@ paths: fmt.Printf("%s: open tracking %t, click tracking %t\n", domain.DomainName, domain.OpenTrackingEnabled, domain.ClickTrackingEnabled) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # changing a tracked attribute and running tofu apply: + # changing a tracked attribute and running terraform apply: # PATCH /api/domains/{domain_id} terraform { required_providers { @@ -3168,9 +3168,9 @@ paths: fmt.Printf("created webhook %d, signing secret %s\n", webhook.ID, webhook.SigningSecret) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the webhook: POST /api/webhooks + # terraform apply creates the webhook: POST /api/webhooks terraform { required_providers { mailtrap = { @@ -3564,9 +3564,9 @@ paths: fmt.Printf("%s (%s): %v\n", webhook.URL, webhook.PayloadFormat, webhook.EventTypes) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu plan refreshes the resource, and tofu import mailtrap_webhook.delivery 1000001 + # terraform plan refreshes the resource, and terraform import mailtrap_webhook.delivery 1000001 # adopts an existing one: GET /api/webhooks/{webhook_id} terraform { required_providers { @@ -3750,9 +3750,9 @@ paths: fmt.Printf("updated webhook %d: %s (active: %t)\n", webhook.ID, webhook.URL, webhook.Active) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # changing a mutable attribute and running tofu apply: + # changing a mutable attribute and running terraform apply: # PATCH /api/webhooks/{webhook_id} terraform { required_providers { @@ -3972,9 +3972,9 @@ paths: fmt.Printf("deleted webhook %d (%s)\n", webhook.ID, webhook.URL) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/webhooks/{webhook_id} terraform { required_providers { diff --git a/specs/sandbox.openapi.yml b/specs/sandbox.openapi.yml index 8f5130f..df4c2cf 100644 --- a/specs/sandbox.openapi.yml +++ b/specs/sandbox.openapi.yml @@ -191,7 +191,7 @@ paths: } } - lang: hcl - label: OpenTofu + label: Terraform source: | # looking a sandbox up by name lists them: GET /api/sandboxes terraform { @@ -382,9 +382,9 @@ paths: fmt.Printf("created project %d (%s)\n", project.ID, project.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the project: POST /api/projects + # terraform apply creates the project: POST /api/projects terraform { required_providers { mailtrap = { @@ -545,7 +545,7 @@ paths: } } - lang: hcl - label: OpenTofu + label: Terraform source: | # looking a project up by name lists them: GET /api/projects terraform { @@ -714,9 +714,9 @@ paths: fmt.Printf("%d %s\n", project.ID, project.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply reads the project: GET /api/projects/{project_id} + # terraform apply reads the project: GET /api/projects/{project_id} terraform { required_providers { mailtrap = { @@ -895,9 +895,9 @@ paths: fmt.Printf("renamed project %d to %s\n", project.ID, project.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # changing the name and running tofu apply: PATCH /api/projects/{project_id} + # changing the name and running terraform apply: PATCH /api/projects/{project_id} terraform { required_providers { mailtrap = { @@ -1071,9 +1071,9 @@ paths: fmt.Println("project deleted") } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/projects/{project_id} terraform { required_providers { @@ -1291,9 +1291,9 @@ paths: fmt.Printf("created sandbox %d (%s)\n", sandbox.ID, sandbox.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the sandbox: POST /api/projects/{project_id}/sandboxes + # terraform apply creates the sandbox: POST /api/projects/{project_id}/sandboxes terraform { required_providers { mailtrap = { @@ -1458,9 +1458,9 @@ paths: fmt.Printf("%s: %s@%s, SMTP %v\n", sandbox.Name, sandbox.Username, sandbox.Domain, sandbox.SMTPPorts) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply reads the sandbox: GET /api/sandboxes/{sandbox_id} + # terraform apply reads the sandbox: GET /api/sandboxes/{sandbox_id} terraform { required_providers { mailtrap = { @@ -1619,9 +1619,9 @@ paths: fmt.Printf("deleted sandbox %d (%s)\n", sandbox.ID, sandbox.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/sandboxes/{sandbox_id} terraform { required_providers { @@ -1827,9 +1827,9 @@ paths: fmt.Printf("updated sandbox %d: %s\n", sandbox.ID, sandbox.Name) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # changing a tracked attribute and running tofu apply: + # changing a tracked attribute and running terraform apply: # PATCH /api/sandboxes/{sandbox_id} terraform { required_providers { diff --git a/specs/templates.openapi.yml b/specs/templates.openapi.yml index 6315eb5..72d01d9 100644 --- a/specs/templates.openapi.yml +++ b/specs/templates.openapi.yml @@ -309,9 +309,9 @@ paths: fmt.Printf("created template %d (uuid %s)\n", template.ID, template.UUID) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu apply creates the template: POST /api/email_templates + # terraform apply creates the template: POST /api/email_templates terraform { required_providers { mailtrap = { @@ -469,9 +469,9 @@ paths: fmt.Printf("%s: %s\n", template.Name, template.Subject) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu plan refreshes the resource, and tofu import mailtrap_email_template.welcome 1000001 + # terraform plan refreshes the resource, and terraform import mailtrap_email_template.welcome 1000001 # adopts an existing one: GET /api/email_templates/{email_template_id} terraform { required_providers { @@ -661,9 +661,9 @@ paths: fmt.Printf("updated template %d: %s\n", template.ID, template.Subject) } - lang: hcl - label: OpenTofu + label: Terraform source: | - # changing a tracked attribute and running tofu apply: PATCH /api/email_templates/{email_template_id} + # changing a tracked attribute and running terraform apply: PATCH /api/email_templates/{email_template_id} terraform { required_providers { mailtrap = { @@ -817,9 +817,9 @@ paths: fmt.Println("template deleted") } - lang: hcl - label: OpenTofu + label: Terraform source: | - # tofu destroy, or removing this block and running tofu apply: + # terraform destroy, or removing this block and running terraform apply: # DELETE /api/email_templates/{email_template_id} terraform { required_providers { From d2b7ea7755c81d5211a9be4fa55e4e7331116b7d Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Thu, 3 Sep 2026 12:30:15 +0200 Subject: [PATCH 8/9] MT-23475: point the SDK repository list at the mailtrap org All six SDKs now live under github.com/mailtrap; .NET and Java were still listed as future references. --- CLAUDE.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a4fbe4d..d6a2510 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -217,11 +217,11 @@ x-codeSamples: Reference SDK repos for accurate code examples: - Node.js: `mailtrap/mailtrap-nodejs` -- PHP: `railsware/mailtrap-php` -- Python: `railsware/mailtrap-python` -- Ruby: `railsware/mailtrap-ruby` -- .NET: Future reference -- Java: Future reference +- PHP: `mailtrap/mailtrap-php` +- Python: `mailtrap/mailtrap-python` +- Ruby: `mailtrap/mailtrap-ruby` +- .NET: `mailtrap/mailtrap-dotnet` +- Java: `mailtrap/mailtrap-java` - Go: `mailtrap/mailtrap-go` - Terraform provider: `mailtrap/terraform-provider-mailtrap` From 4dee553e48198ec04b7f097508b292b3e5f0d9b2 Mon Sep 17 00:00:00 2001 From: Maciej Walusiak Date: Thu, 3 Sep 2026 12:34:25 +0200 Subject: [PATCH 9/9] MT-23475: limit the Terraform samples to creates and data-source reads Update, delete and import are native resource lifecycle and are documented once by the provider, so a per-endpoint sample repeats the resource block without adding anything. Drop those 20 samples, keep the 8 creates and the 6 reads that map to a real data source, and point the create samples at the provider docs on the Terraform registry (review feedback on #55). --- CLAUDE.md | 9 +- specs/account-management.openapi.yml | 50 +---------- specs/contacts.openapi.yml | 122 +-------------------------- specs/email-sending.openapi.yml | 115 +------------------------ specs/sandbox.openapi.yml | 80 +----------------- specs/templates.openapi.yml | 66 +-------------- 6 files changed, 16 insertions(+), 426 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d6a2510..4c4c56a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -205,9 +205,16 @@ x-codeSamples: no `terraform` one; `lang` resolves through Prism identifiers rather than GitBook's documented linguist list, as the existing `csharp` and `shell` samples show. The dropdown tab is titled by `label`, so it reads "Terraform" regardless. +- **Cover only creates and genuine data-source reads.** A `resource` block is the create; its update, + delete and import paths are native Terraform lifecycle and are documented once by the provider, so + those operations get no Terraform sample. A `data` block goes on a read operation only when the + provider has a matching data source. Do not fake a read with a `resource` block plus `terraform import`. - An HCL block is not an API call: a `resource` block declares desired state and the endpoint fires as a side effect of a lifecycle command. Every sample therefore opens with a comment naming both the command - and the operation, e.g. `# terraform apply creates the domain: POST /api/domains`. + and the operation, e.g. `# terraform apply creates the domain: POST /api/domains`. Create samples add a + second comment line linking the provider docs + (`# Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs`), which is + where the lifecycle operations live. - Each sample is self-contained, including the `terraform { required_providers { ... } }` and `provider "mailtrap" {}` blocks, because the sample tab is where a reader learns the source string. - Take attribute names from the provider schema, not from its published examples. Verify with diff --git a/specs/account-management.openapi.yml b/specs/account-management.openapi.yml index c3cfefd..fb01eeb 100644 --- a/specs/account-management.openapi.yml +++ b/specs/account-management.openapi.yml @@ -1199,6 +1199,7 @@ paths: label: Terraform source: | # terraform apply creates the token: POST /api/api_tokens + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -1360,36 +1361,6 @@ paths: fmt.Printf("%s (...%s), created by %s\n", token.Name, token.Last4Digits, token.CreatedBy) } - - lang: hcl - label: Terraform - source: | - # terraform plan refreshes the resource, and terraform import mailtrap_api_token.ci 1000001 - # adopts an existing one: GET /api/api_tokens/{id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - # The token has no update path: every attribute forces replacement. Permission - # drift made outside Terraform is not detected, and token is unavailable after an - # import. - resource "mailtrap_api_token" "ci" { - name = "CI deploy token" - - resources = [ - { - resource_type = "sandbox" - resource_id = 3000001 - access_level = 100 - }, - ] - } parameters: - $ref: '#/components/parameters/api_token_id' responses: @@ -1509,25 +1480,6 @@ paths: fmt.Println("API token deleted") } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/api_tokens/{id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_api_token" "ci" { - name = "CI deploy token" - } parameters: - $ref: '#/components/parameters/api_token_id' responses: diff --git a/specs/contacts.openapi.yml b/specs/contacts.openapi.yml index 65c49de..dacda6e 100644 --- a/specs/contacts.openapi.yml +++ b/specs/contacts.openapi.yml @@ -1784,6 +1784,7 @@ paths: label: Terraform source: | # terraform apply creates the list: POST /api/contacts/lists + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -1922,25 +1923,6 @@ paths: fmt.Printf("%d %s\n", list.ID, list.Name) } - - lang: hcl - label: Terraform - source: | - # terraform plan refreshes the resource, and terraform import mailtrap_contact_list.newsletter 1000001 - # adopts an existing one: GET /api/contacts/lists/{list_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_contact_list" "newsletter" { - name = "Monthly newsletter" - } responses: '200': description: Returns attributes of the contact list. @@ -2058,24 +2040,6 @@ paths: fmt.Printf("renamed list %d to %s\n", list.ID, list.Name) } - - lang: hcl - label: Terraform - source: | - # changing the name and running terraform apply: PATCH /api/contacts/lists/{list_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_contact_list" "newsletter" { - name = "Weekly newsletter" - } requestBody: content: application/json: @@ -2199,25 +2163,6 @@ paths: fmt.Println("contact list deleted") } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/contacts/lists/{list_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_contact_list" "newsletter" { - name = "Monthly newsletter" - } responses: '204': description: Contact List successfully deleted @@ -2470,6 +2415,7 @@ paths: label: Terraform source: | # terraform apply creates the field: POST /api/contacts/fields + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -2642,27 +2588,6 @@ paths: fmt.Printf("%s (%s), merge tag %s\n", field.Name, field.DataType, field.MergeTag) } - - lang: hcl - label: Terraform - source: | - # terraform plan refreshes the resource, and terraform import mailtrap_contact_field.first_name 1000001 - # adopts an existing one: GET /api/contacts/fields/{field_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_contact_field" "first_name" { - name = "First name" - data_type = "text" - merge_tag = "first_name" - } responses: '200': description: Returns attributes of the contact field. @@ -2787,28 +2712,6 @@ paths: fmt.Printf("updated field %d: %s (%s)\n", field.ID, field.Name, field.MergeTag) } - - lang: hcl - label: Terraform - source: | - # changing the name or merge tag and running terraform apply: - # PATCH /api/contacts/fields/{field_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - # data_type is immutable: changing it forces the field to be replaced. - resource "mailtrap_contact_field" "first_name" { - name = "Given name" - data_type = "text" - merge_tag = "given_name" - } requestBody: content: application/json: @@ -2953,27 +2856,6 @@ paths: fmt.Println("contact field deleted") } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/contacts/fields/{field_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_contact_field" "first_name" { - name = "First name" - data_type = "text" - merge_tag = "first_name" - } responses: '204': description: Contact Field successfully deleted diff --git a/specs/email-sending.openapi.yml b/specs/email-sending.openapi.yml index 50f65c0..500a2ce 100644 --- a/specs/email-sending.openapi.yml +++ b/specs/email-sending.openapi.yml @@ -220,6 +220,7 @@ paths: label: Terraform source: | # terraform apply creates the domain: POST /api/domains + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -705,25 +706,6 @@ paths: fmt.Println("domain deleted") } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/domains/{domain_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_domain" "example" { - domain_name = "mail.example.com" - } parameters: - $ref: '#/components/parameters/domain_id' responses: @@ -808,28 +790,6 @@ paths: fmt.Printf("%s: open tracking %t, click tracking %t\n", domain.DomainName, domain.OpenTrackingEnabled, domain.ClickTrackingEnabled) } - - lang: hcl - label: Terraform - source: | - # changing a tracked attribute and running terraform apply: - # PATCH /api/domains/{domain_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_domain" "example" { - domain_name = "mail.example.com" - open_tracking_enabled = true - click_tracking_enabled = false - auto_unsubscribe_link_enabled = true - } parameters: - $ref: '#/components/parameters/domain_id' requestBody: @@ -3171,6 +3131,7 @@ paths: label: Terraform source: | # terraform apply creates the webhook: POST /api/webhooks + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -3563,29 +3524,6 @@ paths: fmt.Printf("%s (%s): %v\n", webhook.URL, webhook.PayloadFormat, webhook.EventTypes) } - - lang: hcl - label: Terraform - source: | - # terraform plan refreshes the resource, and terraform import mailtrap_webhook.delivery 1000001 - # adopts an existing one: GET /api/webhooks/{webhook_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - # An imported webhook has no signing_secret: the API returns it only on create. - resource "mailtrap_webhook" "delivery" { - url = "https://example.com/mailtrap/webhook" - webhook_type = "email_sending" - sending_stream = "transactional" - domain_id = 1000001 - } parameters: - $ref: '#/components/parameters/webhook_id' responses: @@ -3749,33 +3687,6 @@ paths: fmt.Printf("updated webhook %d: %s (active: %t)\n", webhook.ID, webhook.URL, webhook.Active) } - - lang: hcl - label: Terraform - source: | - # changing a mutable attribute and running terraform apply: - # PATCH /api/webhooks/{webhook_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - # Only url, active, payload_format and event_types are mutable; changing - # webhook_type, sending_stream or domain_id replaces the webhook. - resource "mailtrap_webhook" "delivery" { - url = "https://example.com/mailtrap/webhook/v2" - webhook_type = "email_sending" - sending_stream = "transactional" - domain_id = 1000001 - payload_format = "jsonlines" - event_types = ["delivery", "spam_complaint"] - active = false - } parameters: - $ref: '#/components/parameters/webhook_id' requestBody: @@ -3971,28 +3882,6 @@ paths: fmt.Printf("deleted webhook %d (%s)\n", webhook.ID, webhook.URL) } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/webhooks/{webhook_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_webhook" "delivery" { - url = "https://example.com/mailtrap/webhook" - webhook_type = "email_sending" - sending_stream = "transactional" - domain_id = 1000001 - } parameters: - $ref: '#/components/parameters/webhook_id' responses: diff --git a/specs/sandbox.openapi.yml b/specs/sandbox.openapi.yml index df4c2cf..7bd80a3 100644 --- a/specs/sandbox.openapi.yml +++ b/specs/sandbox.openapi.yml @@ -385,6 +385,7 @@ paths: label: Terraform source: | # terraform apply creates the project: POST /api/projects + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -894,24 +895,6 @@ paths: fmt.Printf("renamed project %d to %s\n", project.ID, project.Name) } - - lang: hcl - label: Terraform - source: | - # changing the name and running terraform apply: PATCH /api/projects/{project_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_project" "testing" { - name = "Renamed project" - } requestBody: content: application/json: @@ -1070,25 +1053,6 @@ paths: fmt.Println("project deleted") } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/projects/{project_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_project" "testing" { - name = "My project" - } responses: '200': description: Returns id of the deleted project. @@ -1294,6 +1258,7 @@ paths: label: Terraform source: | # terraform apply creates the sandbox: POST /api/projects/{project_id}/sandboxes + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -1618,26 +1583,6 @@ paths: fmt.Printf("deleted sandbox %d (%s)\n", sandbox.ID, sandbox.Name) } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/sandboxes/{sandbox_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_sandbox" "staging" { - name = "Staging inbox" - project_id = 1000001 - } operationId: deleteSandbox patch: summary: Update a sandbox @@ -1826,27 +1771,6 @@ paths: fmt.Printf("updated sandbox %d: %s\n", sandbox.ID, sandbox.Name) } - - lang: hcl - label: Terraform - source: | - # changing a tracked attribute and running terraform apply: - # PATCH /api/sandboxes/{sandbox_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_sandbox" "staging" { - name = "Renamed inbox" - project_id = 1000001 - email_username = "staging" - } operationId: updateSandbox parameters: - $ref: '#/components/parameters/sandbox_id' diff --git a/specs/templates.openapi.yml b/specs/templates.openapi.yml index 72d01d9..24a9a8b 100644 --- a/specs/templates.openapi.yml +++ b/specs/templates.openapi.yml @@ -312,6 +312,7 @@ paths: label: Terraform source: | # terraform apply creates the template: POST /api/email_templates + # Provider docs: https://registry.terraform.io/providers/mailtrap/mailtrap/latest/docs terraform { required_providers { mailtrap = { @@ -468,27 +469,6 @@ paths: fmt.Printf("%s: %s\n", template.Name, template.Subject) } - - lang: hcl - label: Terraform - source: | - # terraform plan refreshes the resource, and terraform import mailtrap_email_template.welcome 1000001 - # adopts an existing one: GET /api/email_templates/{email_template_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_email_template" "welcome" { - name = "Welcome email" - category = "Onboarding" - subject = "Welcome to Mailtrap" - } responses: '200': description: Returns attributes of the email template. @@ -660,29 +640,6 @@ paths: fmt.Printf("updated template %d: %s\n", template.ID, template.Subject) } - - lang: hcl - label: Terraform - source: | - # changing a tracked attribute and running terraform apply: PATCH /api/email_templates/{email_template_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - # The API cannot clear body_html or body_text once set: a body can only be - # replaced, so removing the argument leaves the stored value in place. - resource "mailtrap_email_template" "welcome" { - name = "Welcome email" - category = "Onboarding" - subject = "Welcome aboard" - body_html = "

Welcome aboard, {{name}}!

" - } requestBody: content: application/json: @@ -816,27 +773,6 @@ paths: fmt.Println("template deleted") } - - lang: hcl - label: Terraform - source: | - # terraform destroy, or removing this block and running terraform apply: - # DELETE /api/email_templates/{email_template_id} - terraform { - required_providers { - mailtrap = { - source = "mailtrap/mailtrap" - } - } - } - - # The token is read from the MAILTRAP_API_TOKEN environment variable. - provider "mailtrap" {} - - resource "mailtrap_email_template" "welcome" { - name = "Welcome email" - category = "Onboarding" - subject = "Welcome to Mailtrap" - } responses: '204': description: No Content