SignNow Electronic Signature Integration on Website

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815

Integrating SignNow electronic signature on a website

SignNow is a competitor to DocuSign from airSlate, focused on SMB and API integrations. It differs with simpler pricing and REST API without mandatory SDK — all operations via standard HTTP requests. Documentation: docs.signnow.com.

Authentication

SignNow uses OAuth 2.0 with client_credentials for server integrations:

class SignNowAuthService
{
    private string $baseUrl = 'https://api.signnow.com';

    public function getToken(): string
    {
        $response = Http::withBasicAuth(
            config('services.signnow.client_id'),
            config('services.signnow.client_secret')
        )->asForm()->post("{$this->baseUrl}/oauth2/token", [
            'grant_type' => 'client_credentials',
            'scope'      => '*',
        ]);

        $token = $response->json('access_token');

        // Cache — token lives for one hour
        Cache::put('signnow_token', $token, now()->addMinutes(55));

        return $token;
    }

    public function token(): string
    {
        return Cache::get('signnow_token') ?? $this->getToken();
    }
}

For flows with a specific user's signature (user-level operations), Password Grant with SignNow user credentials is needed. For most API integrations, client_credentials is sufficient.

Document upload and field placement

class SignNowDocumentService
{
    public function __construct(private SignNowAuthService $auth)
    {}

    public function uploadDocument(string $pdfPath, string $fileName): string
    {
        $response = Http::withToken($this->auth->token())
            ->attach('file', file_get_contents($pdfPath), $fileName)
            ->post('https://api.signnow.com/document');

        return $response->json('id'); // document ID
    }

    public function addSignatureFields(string $documentId, array $signers): void
    {
        $fields = [];
        $roleIndex = 0;

        foreach ($signers as $signer) {
            $fields[] = [
                'type'         => 'signature',
                'role'         => $signer['role'] ?? "Signer {$roleIndex}",
                'role_id'      => (string)$roleIndex,
                'required'     => true,
                'height'       => 40,
                'width'        => 200,
                'x'            => 100,
                'y'            => 600,
                'page_number'  => 0,
            ];
            $roleIndex++;
        }

        Http::withToken($this->auth->token())
            ->put("https://api.signnow.com/document/{$documentId}", [
                'fields' => $fields,
            ]);
    }
}

SignNow supports smart fields via anchor-tags in PDF: if the document contains text like [[sig|req|signer1]], SignNow automatically places a signature field. This is more convenient than fixed coordinates.

Creating invite for signers

public function sendInvite(string $documentId, array $signers): string
{
    $recipients = [];
    foreach ($signers as $index => $signer) {
        $recipients[] = [
            'email'      => $signer['email'],
            'role'       => $signer['role'] ?? "Signer {$index}",
            'role_id'    => (string)$index,
            'order'      => $index + 1,
            'reminder'   => [
                'remind_before'  => 0,
                'remind_after'   => 3, // remind after 3 days
                'remind_repeat'  => 2, // repeat every 2 days
            ],
            'expiration_days' => 30,
            'subject'    => 'Please sign the document',
            'message'    => "Hello, {$signer['name']}! Please sign the attached document.",
        ];
    }

    $response = Http::withToken($this->auth->token())
        ->post("https://api.signnow.com/document/{$documentId}/invite", [
            'to'   => $recipients,
            'from' => config('services.signnow.sender_email'),
        ]);

    return $response->json('status'); // 'success'
}

Embedded signing

For signing without redirecting to SignNow — get a link to embed in iframe or redirect:

public function getSigningLink(string $documentId, string $email): string
{
    $response = Http::withToken($this->auth->token())
        ->post("https://api.signnow.com/link", [
            'document_id' => $documentId,
        ]);

    // link is valid once and for limited time
    return $response->json('url');
}

Embedded signing works via <iframe src="..."> or window.open. After signing, SignNow calls postMessage or redirects to the specified URL.

Webhook: document events

// Register webhook
Http::withToken($this->auth->token())
    ->post('https://api.signnow.com/api/v2/events', [
        'event'       => 'document.complete',
        'entity_id'   => $documentId,
        'action'      => 'callback',
        'callback_url' => route('webhooks.signnow'),
    ]);
// Handler
public function handleSignNowWebhook(Request $request): Response
{
    $data = $request->json()->all();

    if (($data['event'] ?? '') === 'document.complete') {
        $documentId = $data['meta']['document_id'];
        DownloadSignedDocumentJob::dispatch($documentId);
    }

    return response()->noContent();
}

Downloading the signed document

public function downloadSigned(string $documentId): string
{
    $response = Http::withToken($this->auth->token())
        ->get("https://api.signnow.com/document/{$documentId}/download", [
            'type' => 'collapsed', // single PDF with all signatures
        ]);

    $path = storage_path("app/contracts/signed_{$documentId}.pdf");
    file_put_contents($path, $response->body());

    return $path;
}

Differences from DocuSign

SignNow works via pure REST without mandatory SDK, simplifying integration in any language. No built-in fiscal document storage — if you need to store signed contracts with audit trail, this is organized on the application side. Embedded signing functionality is less flexible for UI customization. However, the API is more straightforward and well-documented.

Timeline

OAuth, document upload, sending invite, webhook, and downloading signed file: 2–3 working days. With embedded signing and full UX flow inside the website: 3–4 working days.