Public beta. Create a free workspace today.
Humanize AI text without changing the facts.
Rewrite text and DOCX while keeping numbers, citations, URLs, code and protected terms intact. Try it free, then automate it with the API.
Built for AI products, SaaS teams, content platforms, agencies, automation workflows and document-heavy apps.
# Rewrite a text so it reads naturally
curl -X POST https://api.apidocinfra.com/v1/text/rewrite \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.",
"mode": "natural",
"intensity": "medium",
"protected_terms": ["DocInfra"]
}'# 200 with the rewritten text and metrics
{
"rewrite_id": "rwr_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"text": "Our platform delivers value to 1,200 customers.",
"language": "en",
"mode": "natural",
"intensity": "medium",
"metrics": { "input_words": 18, "output_words": 7, "changed_percent": 61.1 }
}Why DocInfra
Built to survive contact with real documents.
Four guarantees that hold whether you call it from the browser, the dashboard or the API.
Deterministic preservation
Numbers, citations, URLs, code and protected terms survive the rewrite exactly.
Private by design
Content is not written to application logs or used as training data.
API first
Same rewrite engine in the browser, dashboard and REST API.
Text + DOCX
Rewrite plain text or complete Word documents while keeping document structure.
Rewrite APIPublic Beta
Rewrite text through one JSON call.
The same engine behind the Humanizer, available synchronously from your backend: send text, get it back reading naturally with the facts untouched.
- Numbers, dates, links, emails, citations, code and your own protected terms are swapped for markers before the model sees the text and verified on the way back.
- Four styles and three intensities, one endpoint: POST /v1/text/rewrite.
- The text is not stored. Only word counts and timings are kept.
# Rewrite a text so it reads naturally
curl -X POST https://api.apidocinfra.com/v1/text/rewrite \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.",
"mode": "natural",
"intensity": "medium",
"protected_terms": ["DocInfra"]
}'# The same rewrite request from Node or the browser
const response = await fetch("https://api.apidocinfra.com/v1/text/rewrite", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCINFRA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
text: "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.",
mode: "natural",
intensity: "medium",
protected_terms: ["DocInfra"],
}),
});
const result = await response.json(); // { rewrite_id, text, language, mode, intensity, metrics }# The same rewrite request with java.net.http
var payload = """
{"text": "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.", "mode": "natural", "intensity": "medium", "protected_terms": ["DocInfra"]}""";
var request = HttpRequest.newBuilder(URI.create("https://api.apidocinfra.com/v1/text/rewrite"))
.header("Authorization", "Bearer " + System.getenv("DOCINFRA_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
// 200 {"rewrite_id": "...", "text": "...", "language": "en", "mode": "natural", "intensity": "medium", "metrics": {...}}# The same rewrite request with urllib (standard library only)
import json
import os
import urllib.request
payload = {
"text": "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.",
"mode": "natural",
"intensity": "medium",
"protected_terms": ["DocInfra"],
}
request = urllib.request.Request(
"https://api.apidocinfra.com/v1/text/rewrite",
data=json.dumps(payload).encode(),
headers={
"Authorization": "Bearer " + os.environ["DOCINFRA_API_KEY"],
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(request) as response:
result = json.loads(response.read()) # {"rewrite_id": "...", "text": "...", "language": "en", "mode": "natural", "intensity": "medium", "metrics": {...}}# 200 with the rewritten text and metrics
{
"rewrite_id": "rwr_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"text": "Our platform delivers value to 1,200 customers.",
"language": "en",
"mode": "natural",
"intensity": "medium",
"metrics": { "input_words": 18, "output_words": 7, "changed_percent": 61.1 }
}Document RewriteBeta
Rewrite a Word document, keep the document.
Upload a .docx and get a .docx back: same styles, lists, tables, links and images, with the prose rewritten and the structure verified before delivery.
- Async job like every other operation: upload, poll or webhook, then download.
- Headings, fields, links and images are left untouched on purpose.
- The result is reopened and compared with the original before it is delivered.
# Queue a document rewrite job
curl -X POST https://api.apidocinfra.com/v1/documents/rewrite \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: rewrite_123" \
-F "[email protected]" \
-F "mode=professional" \
-F "protected_terms=DocInfra, ISO 27001"
# 202 {"job_id": "...", "status": "queued"}; GET /v1/jobs/{job_id} returns the DOCX download_url# Poll the job, then download the result
GET /v1/jobs/job_01J9Z3F8Q6W2XK4B7C9D0E1F2G
Authorization: Bearer $API_KEY
{
"job_id": "job_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"status": "completed",
"document_id": "doc_01J9Z3F8Q5M8N3P4R5S6T7V8W9",
"created_at": "2026-09-23T15:58:46Z",
"completed_at": "2026-09-23T15:58:50Z",
"download_url": "https://…/processed/…?X-Amz-Expires=300&X-Amz-Signature=…",
"download_url_expires_at": "2026-09-23T16:03:52Z"
}Document ConversionPublic Beta
Convert DOCX and HTML to PDF as a job, not a request.
Upload a file, get a job back at once, download the PDF when it is ready. Same pipeline as every other document capability on this page.
- Async and idempotent: a 202 with a job id, safe to retry with the same Idempotency-Key.
- Scanned for malware before any engine opens it, converted in an isolated container.
- Signed webhooks and a short-lived download link; files are deleted after 24 hours.
# Submit a conversion job
curl -X POST https://api.apidocinfra.com/v1/documents/convert \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: request_123" \
-F "[email protected]" \
-F "output_format=pdf"# The same request from Node or the browser
const form = new FormData();
form.append("file", new Blob([bytes]), "contract.docx");
form.append("output_format", "pdf");
const response = await fetch("https://api.apidocinfra.com/v1/documents/convert", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCINFRA_API_KEY}`, "Idempotency-Key": "request_123" },
body: form,
});
const job = await response.json(); // { job_id, document_id, status: "queued" }# The same request with java.net.http
var boundary = "docinfra-" + UUID.randomUUID();
var body = MultipartBody.of(boundary, Map.of("output_format", "pdf"), Path.of("contract.docx"));
var request = HttpRequest.newBuilder(URI.create("https://api.apidocinfra.com/v1/documents/convert"))
.header("Authorization", "Bearer " + System.getenv("DOCINFRA_API_KEY"))
.header("Idempotency-Key", "request_123")
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(body)
.build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
// 202 {"job_id": "...", "document_id": "...", "status": "queued"}# The same request with urllib (standard library only)
import os
import urllib.request
import uuid
boundary = f"docinfra-{uuid.uuid4()}"
with open("contract.docx", "rb") as f:
file_bytes = f.read()
body = (
f"--{boundary}\r\n"
'Content-Disposition: form-data; name="output_format"\r\n\r\n'
"pdf\r\n"
f"--{boundary}\r\n"
'Content-Disposition: form-data; name="file"; filename="contract.docx"\r\n'
"Content-Type: application/octet-stream\r\n\r\n"
).encode() + file_bytes + f"\r\n--{boundary}--\r\n".encode()
request = urllib.request.Request(
"https://api.apidocinfra.com/v1/documents/convert",
data=body,
headers={
"Authorization": "Bearer " + os.environ["DOCINFRA_API_KEY"],
"Idempotency-Key": "request_123",
"Content-Type": f"multipart/form-data; boundary={boundary}",
},
)
with urllib.request.urlopen(request) as response:
job = response.read() # {"job_id": "...", "document_id": "...", "status": "queued"}# 202 Accepted
{
"job_id": "job_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"document_id": "doc_01J9Z3F8Q5M8N3P4R5S6T7V8W9",
"status": "queued",
"created_at": "2026-09-23T15:58:46Z"
}# Poll the job, then download the result
GET /v1/jobs/job_01J9Z3F8Q6W2XK4B7C9D0E1F2G
Authorization: Bearer $API_KEY
{
"job_id": "job_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"status": "completed",
"document_id": "doc_01J9Z3F8Q5M8N3P4R5S6T7V8W9",
"created_at": "2026-09-23T15:58:46Z",
"completed_at": "2026-09-23T15:58:50Z",
"download_url": "https://…/processed/…?X-Amz-Expires=300&X-Amz-Signature=…",
"download_url_expires_at": "2026-09-23T16:03:52Z"
}Security
Designed with enterprise security requirements in mind.
Documents carry contracts, medical records and financial data. The platform is being built around that from the first line of code.
Encryption in transit
TLS on every public endpoint. No plain HTTP for API traffic.
Isolated workers
Conversion engines run in separate containers, apart from the API.
Malware scanning
Uploaded files are scanned before any engine opens them.
Short retention
Inputs and outputs are kept for 24 hours by default, then deleted automatically.
Document deletion
Delete a document and its derivatives through the API at any time.
API authentication
Scoped API keys stored hashed, with rotation and revocation.
Audit trails
Who did what to which document, and when, per job step.
Rate limiting
Per-key limits that protect your quota and the shared platform.
Private deployments
Planned: keep processing inside your own network when policy requires it.
The public beta already scans every file for malware, converts in isolated containers without internet access, stores API keys hashed, deletes documents automatically after 24 hours (or sooner on demand through the API), delivers signed webhooks for job events, enforces per-key rate limits and keeps an audit trail. The rest is design work for a product that is not generally available. We do not hold SOC 2, ISO 27001, HIPAA or any other certification, and we will not claim one until an independent audit says so.
Private deployment
Run it in our cloud, or inside yours.
Some documents should never leave your network. The same API and processing workers are being packaged to run in your own infrastructure, with the same contract as the hosted service.
- Alpha
Cloud SaaS
Managed by us. The fastest way to start.
- Roadmap
Private Cloud
A dedicated, single-tenant environment operated for you.
- Roadmap
VPC
Processing inside your own cloud account and network boundary.
- Roadmap
Kubernetes
Container images and charts for your existing clusters.
- Roadmap
OpenShift
Packaging aimed at regulated, on-premise enterprise environments.
Data residency. Files, keys and logs stay in the region and network you choose.
Same API. Code written against the hosted service works against a private deployment by changing the base URL.
Your identity provider. Planned integration with your existing OpenID Connect provider.
Roadmap
What comes after conversion and rewrite.
Signatures, verification, extraction and PDF tools are designed for, not running yet.
- Coming soon
PDF Tools
The everyday operations every document pipeline ends up needing.
- Merge and split
- Compression
- Watermarks
- PDF/A
- Coming soon
Sign
Apply digital signatures with keys you control.
- PAdES signatures
- Visible and invisible
- Timestamping
- Coming soon
Verify
Check signature validity and document integrity before you trust a file.
- Signature validation
- Certificate chain checks
- Integrity report
- Roadmap
Extract
Get text and structured data out of scans and PDFs.
- OCR
- Text and layout
- Structured fields as JSON
- Roadmap
Workflows
Chain operations into one job with a single webhook at the end.
- Multi-step pipelines
- Reusable templates
- Per-step audit trail
- Coming soon
- Planned shortly after the beta opens.
- Roadmap
- Designed for, scheduled later.
Nothing in this section is generally available. Status changes here as each capability actually ships.
FAQ
Straight answers.
Where things stand today, and what is still being decided.
What formats are supported?
The public beta converts DOCX and HTML to PDF. XLSX, PPTX, public URLs and common image formats (PNG, JPEG, TIFF) are planned, with PDF/A as an additional output. The final list will depend on what users actually need, so tell us about your formats.
Do you store documents?
Only for as long as a job needs them. Originals and results are kept in private object storage for 24 hours and then deleted automatically. Files flagged by the malware scan are deleted immediately. You can also delete a document and its derivatives on demand with DELETE /v1/documents/{id}.
Will private deployments be available?
Yes, that is part of the plan. The platform is being built so that the API and its processing workers can run inside a customer VPC, on Kubernetes or on OpenShift. It is on the roadmap, not available during the first beta.
Will you support digital signatures?
Yes. Signing PDFs (PAdES) and validating existing signatures are planned right after the core conversion features. Customers will be able to use their own certificates.
Is there a REST API?
The REST API is the product. It is versioned, described with OpenAPI generated from the code, authenticated with API keys, and built around asynchronous jobs, idempotent requests and structured errors. Document Conversion runs today in public beta. SDKs for Java, JavaScript / TypeScript and Python will follow.
When will the beta open?
Signup is open: creating a workspace and an API key does not need an invitation. Document Conversion, the Rewrite API and Document Rewrite already run for every workspace.
How does document security work?
Every file is scanned for malware before a conversion engine opens it, engines run in isolated containers with no internet access, API keys are stored hashed, requests are rate limited per key and every step is recorded in an audit trail. Files travel over TLS in production and are deleted according to the retention policy. We do not hold certifications such as SOC 2 or ISO 27001 today and will say so plainly until that changes.
Pricing
Simple usage-based plans.
Pay for operations, not seats. Start free and move up as your volume grows.
Beta pricing, subject to change.
Free
For trying the API and building a proof of concept.
Free
100 conversions / month
- 5,000 text rewrite words / month
- 2 API keys, 1 webhook
- Community support
Starter
For a first production integration.
$29/month
2,000 conversions / month
- 125,000 text rewrite words / month
- 25,000 document rewrite words / month
- 5 API keys, 5 webhooks
- Email support
Paid plans are coming shortly.
Recommended
Pro
For products where documents are part of the core flow.
$99/month
10,000 conversions / month
- 500,000 text rewrite words / month
- 150,000 document rewrite words / month
- 20 API keys, 20 webhooks
- Priority support
Paid plans are coming shortly.
- Talk to us
EnterpriseCustom
For larger volumes and regulated environments, such as fintech, legaltech and healthtech.
Custom limits · Contact us for enterprise volume and requirements.
An operation is one processing step on one document, for example one conversion or one text rewrite. How multi-step workflows are counted will be defined during the beta. Prices are in US dollars. Refunds and cancellation policy
How billing works
- Plans renew automatically every month, at the price shown, until you cancel.
- Prices are in US dollars. Usage limits apply per billing period.
- Taxes, where they apply, are determined at checkout based on your billing details.
- Cancel anytime from the customer portal; access continues until the end of the period already paid for.
- Lemon Squeezy is our Merchant of Record: it processes the payment and issues the receipt. Your card details never reach DocInfra.
Developers
Async by default. Safe to retry.
Document processing is slow and unpredictable. The API accepts work fast, runs it in a queue and tells you when it is done.
POST /v1/documents/convert
Returns 202 with a job right away. The heavy work happens in a queue.
GET /v1/jobs/{job_id}
Poll the job state: queued, processing, completed, failed or rejected.
EVENT document.completed
Delivers a signed webhook so you do not have to poll at all.
Send an Idempotency-Key with every write. If a network error makes you retry, you get the original job back instead of a duplicate conversion.
API basics
- Authentication
- Every /v1 request needs Authorization: Bearer <api key>. An invalid, revoked or suspended key gets 401 INVALID_API_KEY.
- Errors
- Errors are Problem JSON with a stable code field, such as INVALID_REQUEST, RATE_LIMIT_EXCEEDED, PLAN_LIMIT_EXCEEDED, PRESERVATION_FAILED and REWRITE_CHECK_FAILED. Read the code, not the message.
- Limits
- Up to 10,000 words per text rewrite, 25 MB per document, and a per-key rate limit (20 writes every 10 seconds, 600 per hour).
- Idempotency
- Idempotency-Key is required on document conversion and document rewrite, optional on text rewrite. Retrying the same key and request returns the original job instead of a duplicate.
- Webhooks
- Signed with a secret unique to each endpoint; they deliver document.completed, document.failed, document.rejected and document.deleted.
- Protected terms
- Pass protected_terms, or rely on the preserve defaults, to keep numbers, URLs, emails, citations, code and named terms exactly as written.
- Plans
- Every workspace has a plan with its own quotas. Pricing
# Submit a conversion job
curl -X POST https://api.apidocinfra.com/v1/documents/convert \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: request_123" \
-F "[email protected]" \
-F "output_format=pdf"# The same request from Node or the browser
const form = new FormData();
form.append("file", new Blob([bytes]), "contract.docx");
form.append("output_format", "pdf");
const response = await fetch("https://api.apidocinfra.com/v1/documents/convert", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCINFRA_API_KEY}`, "Idempotency-Key": "request_123" },
body: form,
});
const job = await response.json(); // { job_id, document_id, status: "queued" }# The same request with java.net.http
var boundary = "docinfra-" + UUID.randomUUID();
var body = MultipartBody.of(boundary, Map.of("output_format", "pdf"), Path.of("contract.docx"));
var request = HttpRequest.newBuilder(URI.create("https://api.apidocinfra.com/v1/documents/convert"))
.header("Authorization", "Bearer " + System.getenv("DOCINFRA_API_KEY"))
.header("Idempotency-Key", "request_123")
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(body)
.build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
// 202 {"job_id": "...", "document_id": "...", "status": "queued"}# The same request with urllib (standard library only)
import os
import urllib.request
import uuid
boundary = f"docinfra-{uuid.uuid4()}"
with open("contract.docx", "rb") as f:
file_bytes = f.read()
body = (
f"--{boundary}\r\n"
'Content-Disposition: form-data; name="output_format"\r\n\r\n'
"pdf\r\n"
f"--{boundary}\r\n"
'Content-Disposition: form-data; name="file"; filename="contract.docx"\r\n'
"Content-Type: application/octet-stream\r\n\r\n"
).encode() + file_bytes + f"\r\n--{boundary}--\r\n".encode()
request = urllib.request.Request(
"https://api.apidocinfra.com/v1/documents/convert",
data=body,
headers={
"Authorization": "Bearer " + os.environ["DOCINFRA_API_KEY"],
"Idempotency-Key": "request_123",
"Content-Type": f"multipart/form-data; boundary={boundary}",
},
)
with urllib.request.urlopen(request) as response:
job = response.read() # {"job_id": "...", "document_id": "...", "status": "queued"}# 202 Accepted
{
"job_id": "job_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"document_id": "doc_01J9Z3F8Q5M8N3P4R5S6T7V8W9",
"status": "queued",
"created_at": "2026-09-23T15:58:46Z"
}# Poll the job, then download the result
GET /v1/jobs/job_01J9Z3F8Q6W2XK4B7C9D0E1F2G
Authorization: Bearer $API_KEY
{
"job_id": "job_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"status": "completed",
"document_id": "doc_01J9Z3F8Q5M8N3P4R5S6T7V8W9",
"created_at": "2026-09-23T15:58:46Z",
"completed_at": "2026-09-23T15:58:50Z",
"download_url": "https://…/processed/…?X-Amz-Expires=300&X-Amz-Signature=…",
"download_url_expires_at": "2026-09-23T16:03:52Z"
}# Signed event delivered to your endpoint
POST https://your-app.com/webhooks/documents
Content-Type: application/json
X-DocInfra-Event-Id: evt_01J9Z3F8Q6W2XK4B7C9D0E1F2G
X-DocInfra-Timestamp: 1790179134
X-DocInfra-Signature: v1=5f2b...
{
"id": "evt_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"type": "document.completed",
"created_at": "2026-09-23T15:58:50Z",
"data": {
"document_id": "doc_01J9Z3F8Q5M8N3P4R5S6T7V8W9",
"job_id": "job_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"status": "completed"
}
}# Rewrite a text so it reads naturally
curl -X POST https://api.apidocinfra.com/v1/text/rewrite \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.",
"mode": "natural",
"intensity": "medium",
"protected_terms": ["DocInfra"]
}'# The same rewrite request from Node or the browser
const response = await fetch("https://api.apidocinfra.com/v1/text/rewrite", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCINFRA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
text: "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.",
mode: "natural",
intensity: "medium",
protected_terms: ["DocInfra"],
}),
});
const result = await response.json(); // { rewrite_id, text, language, mode, intensity, metrics }# The same rewrite request with java.net.http
var payload = """
{"text": "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.", "mode": "natural", "intensity": "medium", "protected_terms": ["DocInfra"]}""";
var request = HttpRequest.newBuilder(URI.create("https://api.apidocinfra.com/v1/text/rewrite"))
.header("Authorization", "Bearer " + System.getenv("DOCINFRA_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
// 200 {"rewrite_id": "...", "text": "...", "language": "en", "mode": "natural", "intensity": "medium", "metrics": {...}}# The same rewrite request with urllib (standard library only)
import json
import os
import urllib.request
payload = {
"text": "In today’s fast-paced world, it is important to note that our platform delivers value to 1,200 customers.",
"mode": "natural",
"intensity": "medium",
"protected_terms": ["DocInfra"],
}
request = urllib.request.Request(
"https://api.apidocinfra.com/v1/text/rewrite",
data=json.dumps(payload).encode(),
headers={
"Authorization": "Bearer " + os.environ["DOCINFRA_API_KEY"],
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(request) as response:
result = json.loads(response.read()) # {"rewrite_id": "...", "text": "...", "language": "en", "mode": "natural", "intensity": "medium", "metrics": {...}}# 200 with the rewritten text and metrics
{
"rewrite_id": "rwr_01J9Z3F8Q6W2XK4B7C9D0E1F2G",
"text": "Our platform delivers value to 1,200 customers.",
"language": "en",
"mode": "natural",
"intensity": "medium",
"metrics": { "input_words": 18, "output_words": 7, "changed_percent": 61.1 }
}This is the public beta contract.
Developer experience roadmap
SDKs planned for Java, JavaScript / TypeScript, Python.
REST API
AlphaVersioned, predictable resources under /v1.
OpenAPI
AlphaGenerated from the code, so it always matches the API.
API keys
AlphaStored hashed, with test and live prefixes. Rotation comes next.
Async jobs
AlphaEvery heavy operation returns a job you can poll.
Idempotency
AlphaRetry safely with an Idempotency-Key header.
Structured errors
AlphaStable error codes, never just a status line.
Webhooks
AlphaSigned events instead of polling.
Sandbox
Coming soonTest keys that never touch billing.
SDKs
RoadmapJava, JavaScript / TypeScript and Python.