Developer How-To · Dart / Flutter

Add e-signatures to
a Dart or Flutter app

Four steps with http and crypto — no SDK, no native code. Works on Flutter mobile, web, desktop, and server-side Dart.

1

Add dependencies

Add http for HTTP requests and crypto for HMAC-SHA256. Both are standard Dart/Flutter packages with no native code dependencies.

dart
# pubspec.yaml
dependencies:
  http: ^1.2.0
  crypto: ^3.0.3

# dart pub get
2

Authenticate with OAuth2

Exchange client credentials for a bearer token. Cache the token and refresh before the 3600-second expiry. In Flutter, store credentials in flutter_secure_storage — never hardcode them.

dart
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<String> getToken(String clientId, String clientSecret) async {
  final response = await http.post(
    Uri.parse('https://api.getsigned.app/oauth/token'),
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    body: {
      'grant_type':    'client_credentials',
      'client_id':     clientId,
      'client_secret': clientSecret,
    },
  );
  if (response.statusCode != 200) throw Exception('Auth failed: ${response.body}');
  return jsonDecode(response.body)['access_token'] as String;
}
3

Create an envelope

Upload the PDF as a multipart request with signer details and field coordinates. The API returns the envelope ID you use to send.

dart
Future<String> createEnvelope({
  required String token,
  required List<int> pdfBytes,
  required String signerName,
  required String signerEmail,
}) async {
  final request = http.MultipartRequest(
    'POST',
    Uri.parse('https://api.getsigned.app/v1/envelopes'),
  )
    ..headers['Authorization'] = 'Bearer $token'
    ..fields['signers'] =
        jsonEncode([{'name': signerName, 'email': signerEmail}])
    ..fields['fields'] =
        jsonEncode([{'type': 'signature', 'page': 1, 'x': 300, 'y': 580, 'w': 200, 'h': 60}])
    ..files.add(http.MultipartFile.fromBytes(
      'document', pdfBytes,
      filename: 'document.pdf',
      contentType: MediaType('application', 'pdf'),
    ));

  final streamed = await request.send();
  final body = await streamed.stream.bytesToString();
  if (streamed.statusCode != 201) throw Exception('Create failed: $body');
  return jsonDecode(body)['id'] as String;
}
4

Send and verify webhooks

Call /send to dispatch the signing link. When the signer completes, GetSigned POSTs to your server. Verify the HMAC-SHA256 signature with the crypto package before processing.

dart
import 'package:crypto/crypto.dart';

Future<void> sendEnvelope(String token, String envelopeId) async {
  final response = await http.post(
    Uri.parse('https://api.getsigned.app/v1/envelopes/$envelopeId/send'),
    headers: {'Authorization': 'Bearer $token'},
  );
  if (response.statusCode != 200) throw Exception('Send failed: ${response.body}');
}

// In your server-side Dart webhook handler (shelf / dart_frog):
bool verifyHmac(List<int> rawBody, String sigHeader, String secret) {
  final hmac = Hmac(sha256, utf8.encode(secret));
  final computed = hmac.convert(rawBody).toString();
  // Constant-time compare — accumulate differences, never return early
  if (computed.length != sigHeader.length) return false;
  var diff = 0;
  for (var i = 0; i < computed.length; i++) {
    diff |= computed.codeUnitAt(i) ^ sigHeader.codeUnitAt(i);
  }
  return diff == 0;
}

// envelope.completed → download sealed PDF:
// GET /v1/envelopes/$id/document
// Authorization: Bearer $token

Frequently asked questions

Should I call GetSigned directly from a Flutter mobile app?

No. Never embed OAuth2 client credentials in a Flutter app — they can be extracted from the compiled binary. The correct architecture is: your Flutter app calls your own backend API, and your backend calls GetSigned with the credentials stored server-side. The Dart code in this guide is intended for a server-side Dart backend (shelf, dart_frog) or for your backend team to reference. If you need to trigger signing from a Flutter UI, expose an endpoint in your backend that your app calls.

Can I use this guide for both Flutter and server-side Dart?

Yes. The http and crypto packages work on all Dart platforms: Flutter (iOS + Android + web + desktop), the Dart CLI, and Dart server runtimes. The multipart upload pattern and HMAC verification are identical across targets. The only platform-specific consideration is where credentials are stored — flutter_secure_storage for Flutter apps (which call your backend), environment variables or a secret manager for Dart server deployments.

How do I handle the MediaType import for multipart uploads?

MediaType is in the http_parser package, which is a transitive dependency of http — no extra declaration needed in pubspec.yaml. Import it with: import "package:http_parser/http_parser.dart"; If you get a MediaType not found error, add http_parser: ^4.0.0 to your pubspec.yaml explicitly.

How do I verify webhooks in a shelf or dart_frog server?

Read the raw request body as bytes before any parsing — JSON decoding can alter whitespace and invalidate the HMAC. In shelf: use request.read().toList() to get the byte stream, then Uint8List.fromList(bytes.expand((x) => x).toList()). Extract the X-GetSigned-Signature header, call verifyHmac() with the raw bytes and the header value. Only proceed to parse the JSON payload after the signature is verified.

Is there a Dart SDK for GetSigned?

No dedicated SDK — the integration uses the standard http and crypto packages directly. The full integration is under 100 lines of idiomatic Dart. Using the REST API directly means no SDK version conflicts, no additional transitive dependencies, and no abstraction between your code and the API.

Related: Swift guide · Kotlin guide · Node.js guide · Webhook guide

Send your first envelope today

Free tier — 25 envelopes per month. Full API access from day one.

Get free API keys →