sent-dm-java
Used in:
components
- OverviewOverview
- VersionsVersions
- DependentsDependents
- DependenciesDependencies
<dependency>
<groupId>dm.sent</groupId>
<artifactId>sent-dm-java</artifactId>
<version>0.9.0</version>
</dependency><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>dm.sent</groupId>
<artifactId>sent-dm-java</artifactId>
<version>0.9.0</version>
<name>Sent DM API - v3</name>
<description>The Sent DM API enables programmatic SMS and WhatsApp messaging with contact
management, template-based messaging, compliance (10DLC brand/campaign
registration), and webhook delivery tracking.
## Authentication
All requests require the `x-api-key` header with your API key (e.g.
`sk_live_...` for production, `sk_test_...` for sandbox). Your account is
identified automatically from the key.
## Profile Scoping
Organization API keys can act on behalf of a child profile by passing the
optional `x-profile-id` header with the profile's UUID. When present, all
operations are scoped to that profile — contacts, messages, templates, and other
resources are read and written as if the profile's own API key were used.
- Only **organization** API keys may use this header. Profile API keys will
receive `403`.
- The profile must belong to the calling organization, otherwise `404` is
returned.
- When profile-scoped, the `X-Profile-Id` response header echoes the profile
UUID.
- Rate limits remain on the organization's pool regardless of profile scoping.
## Idempotency
POST, PUT, and PATCH requests accept an optional `Idempotency-Key` header. When
provided, the API guarantees at-most-once execution: duplicate requests with the
same key return the original response. Keys are scoped per customer and expire
after 24 hours.
## Sandbox Mode
All mutation endpoints (POST, PUT, DELETE) accept an optional `sandbox` field in
the request body. When set to `true`, the API validates the request and returns
a realistic fake response **without executing any side effects** — no database
writes, no messages sent, no external API calls.
Sandbox mode is useful for:
- **Integration testing** — verify your request payloads and response parsing
without affecting real data
- **CI/CD pipelines** — run end-to-end tests against the live API without
consuming resources
- **Client development** — build and test your integration before going live
```json
{
"sandbox": true,
"phone_number": "+1234567890"
}
```
Sandbox mode responses include the `X-Sandbox: true` header and return the same
schema as real responses with sample data. Authentication and validation still
run normally — only the service-layer execution is skipped.
## Rate Limiting
API requests are rate-limited per customer. Standard endpoints allow **200
requests/minute**; sensitive endpoints (e.g. secret rotation) allow **10
requests/minute**. When limits are exceeded, the API returns
`429 Too Many Requests`.
Rate limit headers on `429` responses: | Header | Description |
|--------|-------------| | `Retry-After` | Seconds until you can retry | |
`X-RateLimit-Limit` | Maximum requests allowed in the window | |
`X-RateLimit-Remaining` | Requests remaining (always `0` on 429) | |
`X-RateLimit-Reset` | Unix timestamp when the window resets |
## Common Response Headers
All responses include these headers: | Header | Description |
|--------|-------------| | `X-Request-Id` | Unique request identifier for
support and debugging | | `X-Response-Time` | Server processing time (e.g.
`12ms`) | | `X-API-Version` | API version (always `v3`) | | `X-Profile-Id` |
Echoed profile UUID when the request is profile-scoped via `x-profile-id` |
Idempotent replay responses also include: | Header | Description |
|--------|-------------| | `Idempotent-Replayed` | `true` if this is a cached
replay | | `X-Original-Request-Id` | Request ID of the original request |
## Errors
All errors follow a consistent JSON envelope:
```json
{
"success": false,
"error": {
"code": "RESOURCE_001",
"message": "Contact not found",
"doc_url": "https://docs.sent.dm/errors/RESOURCE_001"
},
"meta": { "request_id": "req_...", "timestamp": "...", "version": "v3" }
}
```
### Error Code Reference
| Code | Description |
| ------------------ | ---------------------------------------------- |
| **Authentication** | |
| `AUTH_001` | User is not authenticated |
| `AUTH_002` | Invalid or expired API key |
| `AUTH_004` | Insufficient permissions for this operation |
| **Validation** | |
| `VALIDATION_001` | Request validation failed |
| `VALIDATION_002` | Invalid phone number format |
| `VALIDATION_003` | Invalid GUID format |
| `VALIDATION_004` | Required field is missing |
| `VALIDATION_005` | Field value out of valid range |
| `VALIDATION_006` | Invalid enum value |
| `VALIDATION_007` | Invalid Idempotency-Key format |
| **Resource** | |
| `RESOURCE_001` | Contact not found |
| `RESOURCE_002` | Template not found |
| `RESOURCE_003` | Message not found |
| `RESOURCE_004` | Customer not found |
| `RESOURCE_005` | Organization not found |
| `RESOURCE_006` | User not found |
| `RESOURCE_007` | Resource already exists (duplicate) |
| `RESOURCE_008` | Webhook not found |
| **Business Logic** | |
| `BUSINESS_001` | Cannot modify inherited contact |
| `BUSINESS_002` | Rate limit exceeded |
| `BUSINESS_003` | Insufficient account balance |
| `BUSINESS_004` | Contact has opted out of messaging |
| `BUSINESS_005` | Template not approved for sending |
| `BUSINESS_006` | Message cannot be modified in current state |
| `BUSINESS_007` | Channel not available for this contact |
| `BUSINESS_008` | Operation would exceed quota |
| **Conflict** | |
| `CONFLICT_001` | Concurrent idempotent request in progress |
| **Service** | |
| `SERVICE_001` | Cache service temporarily unavailable |
| **Internal** | |
| `INTERNAL_001` | Unexpected internal server error |
| `INTERNAL_002` | Database operation failed |
| `INTERNAL_003` | External service error (SMS/WhatsApp provider) |
| `INTERNAL_004` | Timeout waiting for operation |
| `INTERNAL_005` | Service temporarily unavailable |</description>
<url>https://docs.sent.dm</url>
<licenses>
<license>
<name>Apache-2.0</name>
</license>
</licenses>
<developers>
<developer>
<name>Sent Dm</name>
<email>support@sent.dm</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/sentdm/sent-dm-java.git</connection>
<developerConnection>scm:git:git://github.com/sentdm/sent-dm-java.git</developerConnection>
<url>https://github.com/sentdm/sent-dm-java</url>
</scm>
<dependencies>
<dependency>
<groupId>dm.sent</groupId>
<artifactId>sent-dm-java-client-okhttp</artifactId>
<version>0.9.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.8.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>