Spring Boot RestTemplate POST JSON with a Bearer token
Set JSON content type and setBearerAuth on HttpHeaders, wrap them in an HttpEntity, call postForEntity, and catch the 4xx exception to read the body.

To POST JSON with Spring Boot's RestTemplate, create HttpHeaders, call setContentType(MediaType.APPLICATION_JSON) and setBearerAuth(token), wrap the body and headers in an HttpEntity, and pass it to restTemplate.postForEntity(url, entity, String.class). A 4xx reply doesn't come back as a response: RestTemplate throws HttpClientErrorException, so catch it and read getResponseBodyAsString() for the API's error.
Spring facts come from the Javadoc for RestTemplate, HttpHeaders, DefaultResponseErrorHandler and RestClientResponseException, the REST Clients reference and Spring Boot's Calling REST Services. The example API is Sume's, from Authentication, Video Generation, Errors and rate limits and Jobs and results. All were read on 2026-09-29. Sume's only documented client library is the TypeScript SDK, so from Java this is a plain HTTPS call.
How do I POST a JSON body with a Bearer token in RestTemplate?
Spring Boot doesn't provide a single auto-configured RestTemplate bean; it auto-configures a RestTemplateBuilder you inject and build from, where you can also set connect and read timeouts. This service starts a Sume video job with the key from the server environment.
@Service
public class VideoClient {
private final RestTemplate rest;
public VideoClient(RestTemplateBuilder builder) {
this.rest = builder.connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(30)).build();
}
public String startVideo(String idempotencyKey) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(System.getenv("SUME_API_KEY"));
headers.set("Idempotency-Key", idempotencyKey);
Map<String, Object> body = Map.of("model", "sume/auto",
"prompt", "A vertical product clip on a desk",
"aspect_ratio", "9:16", "duration", 5);
try {
ResponseEntity<String> res = rest.postForEntity(
"https://api.sume.com/v1/videos", new HttpEntity<>(body, headers), String.class);
return res.getBody(); // 202: id, polling_url, status "pending"
} catch (HttpStatusCodeException e) { // 4xx and 5xx
throw new IllegalStateException(e.getStatusCode() + " " + e.getResponseBodyAsString(), e);
}
}
}Why does postForEntity throw on a 400 or 401?
By default RestTemplate uses DefaultResponseErrorHandler, which treats any 4xx or 5xx status as an error and throws. Catch HttpStatusCodeException, the base class of both, or the specific one you care about. getResponseBodyAsString() returns the body, which for a JSON API holds the reason.
Sume's errors share one envelope, {"error": {"code", "message", "request_id", "details"}}, and the request id is safe to share with Sume support.
| Status | Spring throws | Sume code and meaning |
|---|---|---|
400 | HttpClientErrorException.BadRequest | invalid_request: body, query, path or headers invalid |
401 | HttpClientErrorException.Unauthorized | unauthorized: key missing or invalid, or both Authorization and x-api-key sent |
402 | HttpClientErrorException | insufficient_credits: the balance can't cover the generation |
415 | HttpClientErrorException.UnsupportedMediaType | unsupported_media_type: the body wasn't application/json |
429 | HttpClientErrorException.TooManyRequests | rate_limited or queue_full: back off, honor retry-after |
503 | HttpServerErrorException.ServiceUnavailable | provider_not_configured or provider_capacity_exceeded: a runtime dependency is unavailable or at capacity |
Is RestTemplate deprecated?
Yes, as of Spring Framework 7.0: the REST Clients reference says RestTemplate is deprecated in favor of RestClient and will be removed in a future version. Spring Boot's docs still cover RestTemplate for existing code you don't want to migrate. The migration guidance is to create a RestClient from an existing template with RestClient.create(restTemplate) and replace RestTemplate usage component by component. In RestClient the same call is restClient.post().uri(url).contentType(APPLICATION_JSON).body(body).retrieve(), and it also throws on 4xx and 5xx unless you override that with onStatus.
Why does the POST return before the video is ready?
Video generation is asynchronous. POST /v1/videos answers 202 Accepted with id, polling_url and status: "pending", and you poll GET /v1/videos/{id} with backoff until it's completed. Keep the read timeout short, since a client-side timeout doesn't cancel the job: it keeps running and still bills.
The Idempotency-Key header is what makes a retry safe. On /v1/videos, a replay with the key returns the original job, and Sume's docs say not to retry unsafe submit requests without one. Derive the key from the order or record, not a random UUID per attempt. Spring Retry for paid API calls covers the retry side, and Spring Boot webhook HMAC verification covers receiving the result.
Sources
- Authentication
- Video Generation
- Errors and rate limits
- Jobs and results
- Spring Framework Javadoc: RestTemplate (read 2026-09-29)
- Spring Framework Javadoc: HttpHeaders (read 2026-09-29)
- Spring Framework Javadoc: DefaultResponseErrorHandler (read 2026-09-29)
- Spring Framework Javadoc: RestClientResponseException (read 2026-09-29)
- Spring Framework reference: REST Clients (read 2026-09-29)
- Spring Boot reference: Calling REST Services (read 2026-09-29)
Related posts
More in Integrations
- Runway MCP for Grok Bot: what shipped and what Sume lists
Runway added an MCP plugin to Grok Bot on Sep 17, 2026 for paid plans. Sume's docs do not list Grok Bot; here is how to test whether any client can use its MCP.
- Runway MCP in Cursor: what shipped and running a second server
Runway MCP reached Cursor's Marketplace on Sep 23, 2026. Cursor can hold more than one MCP server; here is how a second, Sume's hosted MCP, is added and scoped.
- Salesforce Apex HTTP callout: POST JSON to an external API
An Apex HTTP callout builds an HttpRequest, sends it with Http.send and reads the HttpResponse. From a trigger or after DML, it must run asynchronously.
- Snapchat Ads MCP client ids: claude-snap-ads, codex-snap-ads, more
Each agent has its own Snapchat Ads MCP client id: claude-snap-ads, codex-snap-ads, chatgpt-snap-ads, gemini-snap-ads and antigravity-snap-ads. Where each goes.
Written by Sume